mirror of https://github.com/abpframework/abp.git
committed by
GitHub
9 changed files with 245 additions and 159 deletions
@ -0,0 +1,197 @@ |
|||
# HTTP Error Handling |
|||
|
|||
When the `RestService` is used, all HTTP errors are reported to the [`HttpErrorReporterService`](./HTTP-Error-Reporter-Service), and then `ErrorHandler`, a service exposed by the `@abp/ng.theme.shared` package automatically handles the errors. |
|||
|
|||
## Custom HTTP Error Handler |
|||
|
|||
### Function Method `Deprecated` |
|||
|
|||
A custom HTTP error handler can be registered to an injection token named `HTTP_ERROR_HANDLER`. If a custom handler function is registered, the `ErrorHandler` executes that function. |
|||
|
|||
See an example: |
|||
|
|||
```ts |
|||
// http-error-handler.ts |
|||
import { ContentProjectionService, PROJECTION_STRATEGY } from '@abp/ng.core'; |
|||
import { ToasterService } from '@abp/ng.theme.shared'; |
|||
import { HttpErrorResponse } from '@angular/common/http'; |
|||
import { Injector } from '@angular/core'; |
|||
import { of, EMPTY } from 'rxjs'; |
|||
import { Error404Component } from './error404/error404.component'; |
|||
|
|||
export function handleHttpErrors(injector: Injector, httpError: HttpErrorResponse) { |
|||
if (httpError.status === 400) { |
|||
const toaster = injector.get(ToasterService); |
|||
toaster.error(httpError.error?.error?.message || 'Bad request!', '400'); |
|||
return EMPTY; |
|||
} |
|||
|
|||
if (httpError.status === 404) { |
|||
const contentProjection = injector.get(ContentProjectionService); |
|||
contentProjection.projectContent(PROJECTION_STRATEGY.AppendComponentToBody(Error404Component)); |
|||
return EMPTY; |
|||
} |
|||
|
|||
return of(httpError); |
|||
} |
|||
|
|||
// app.module.ts |
|||
import { Error404Component } from './error404/error404.component'; |
|||
import { handleHttpErrors } from './http-error-handling'; |
|||
import { HTTP_ERROR_HANDLER, ... } from '@abp/ng.theme.shared'; |
|||
|
|||
@NgModule({ |
|||
// ... |
|||
providers: [ |
|||
// ... |
|||
{ provide: HTTP_ERROR_HANDLER, useValue: handleHttpErrors } |
|||
], |
|||
declarations: [ |
|||
//... |
|||
Error404Component], |
|||
}) |
|||
export class AppModule {} |
|||
``` |
|||
|
|||
In the example above: |
|||
|
|||
- Created a function named `handleHttpErrors` and defined as value of the `HTTP_ERROR_HANDLER` provider in app.module. After this, the function executes when an HTTP error occurs. |
|||
- 400 bad request errors is handled. When a 400 error occurs. |
|||
|
|||
- Since `of(httpError)` is returned at bottom of the `handleHttpErrors`, the `ErrorHandler` will handle the HTTP errors except 400 and 404 errors. |
|||
|
|||
**Note 1:** If you put `return EMPTY` to next line of handling an error, default error handling will not work for that error. [EMPTY](https://rxjs.dev/api/index/const/EMPTY) can be imported from `rxjs`. |
|||
|
|||
```ts |
|||
export function handleHttpErrors( |
|||
injector: Injector, |
|||
httpError: HttpErrorResponse |
|||
) { |
|||
if (httpError.status === 403) { |
|||
// handle 403 errors here |
|||
return EMPTY; // put return EMPTY to skip default error handling |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**Note 2:** If you put `return of(httpError)`, default error handling will work. |
|||
|
|||
- `of` is a function. It can be imported from `rxjs`. |
|||
- `httpError` is the second parameter of the error handler function which is registered to the `HTTP_ERROR_HANDLER` provider. Type of the `httpError` is `HttpErrorResponse`. |
|||
|
|||
```ts |
|||
import { of } from "rxjs"; |
|||
|
|||
export function handleHttpErrors( |
|||
injector: Injector, |
|||
httpError: HttpErrorResponse |
|||
) { |
|||
if (httpError.status === 500) { |
|||
// handle 500 errors here |
|||
} |
|||
|
|||
// you can return the of(httpError) at bottom of the function to run the default handler of ABP for HTTP errors that you didn't handle above. |
|||
return of(httpError); |
|||
} |
|||
``` |
|||
|
|||
### Service Method |
|||
|
|||
You can provide **more than one handler** with services, a custom HTTP error handler service can be registered with injection token named **`CUSTOM_ERROR_HANDLERS`**. ABP has some default [error handlers](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/theme-shared/src/lib/providers/error-handlers.provider.ts). |
|||
|
|||
### How To Add New Handler Service |
|||
|
|||
ABP error handler services are implements the interface of **CustomHttpErrorHandlerService**. |
|||
|
|||
**Interface of `CUSTOM_ERROR_HANDLERS`** |
|||
|
|||
```ts |
|||
interface CustomHttpErrorHandlerService { |
|||
readonly priority: number; |
|||
canHandle(error: unknown): boolean; |
|||
execute(): void; |
|||
} |
|||
``` |
|||
|
|||
- **`priority`** ABP sorts the services according to the number of the priority variable. Higher priority will be checked first. |
|||
- **`canHandle`** Check if the service can handle the error. Returns boolean. |
|||
- **`execute`** If the service can handle the error, then run the execute method. |
|||
|
|||
**In Summary** |
|||
|
|||
- Services are sorted by their priority number. |
|||
- Start from highest priority service and run canHandle() method. Pick the service if can handle the error, if not check next service. |
|||
- If the service found, run the execute method of a service. Done. |
|||
|
|||
See an example: |
|||
|
|||
```ts |
|||
// custom-error-handler.service.ts |
|||
import { inject, Injectable } from "@angular/core"; |
|||
import { HttpErrorResponse } from "@angular/common/http"; |
|||
import { CustomHttpErrorHandlerService } from "@abp/ng.theme.shared"; |
|||
import { CUSTOM_HTTP_ERROR_HANDLER_PRIORITY } from "@abp/ng.theme.shared"; |
|||
import { ToasterService } from "@abp/ng.theme.shared"; |
|||
|
|||
@Injectable({ providedIn: "root" }) |
|||
export class MyCustomErrorHandlerService |
|||
implements CustomHttpErrorHandlerService |
|||
{ |
|||
// You can write any number here, ex: 9999 |
|||
readonly priority = CUSTOM_HTTP_ERROR_HANDLER_PRIORITY.veryHigh; |
|||
protected readonly toaster = inject(ToasterService); |
|||
private error: HttpErrorResponse | undefined = undefined; |
|||
|
|||
// What kind of error should be handled by this service? You can decide it in this method. If error is suitable to your case then return true; otherwise return false. |
|||
canHandle(error: unknown): boolean { |
|||
if (error instanceof HttpErrorResponse && error.status === 400) { |
|||
this.error = error; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// If this service is picked from ErrorHandler, this execute method will be called. |
|||
execute() { |
|||
this.toaster.error( |
|||
this.error.error?.error?.message || "Bad request!", |
|||
"400" |
|||
); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
```ts |
|||
|
|||
// app.module.ts |
|||
import { CUSTOM_ERROR_HANDLERS, ... } from '@abp/ng.theme.shared'; |
|||
import { MyCustomErrorHandlerService } from './custom-error-handler.service'; |
|||
|
|||
@NgModule({ |
|||
// ... |
|||
providers: [ |
|||
// ... |
|||
{ |
|||
provide: CUSTOM_ERROR_HANDLERS, |
|||
useExisting: MyCustomErrorHandlerService, |
|||
multi: true, |
|||
} |
|||
] |
|||
}) |
|||
export class AppModule {} |
|||
``` |
|||
|
|||
In the example above: |
|||
|
|||
- Created a service named `MyCustomErrorHandlerService`, and provided via `useExisting` key because we dont want another instance of it. And set `multi` key to true because ABP default error handlers are also provided with **CUSTOM_ERROR_HANDLERS** injection token. |
|||
|
|||
- 400 errors are handled from custom `MyCustomErrorHandlerService`. When a 400 error occurs, backend error message will be displayed as shown below: |
|||
|
|||
 |
|||
|
|||
### Notes |
|||
|
|||
- If your service cannot handle the error. Then ABP will check the next Error Service. |
|||
- If none of the service handle the error. Then basic confirmation message about the error will be shown to the user. |
|||
- You can provide more than one service, with CUSTOM_ERROR_HANDLER injection token. |
|||
- If you want your custom service to be evaluated (checked) earlier, set the priority variable high. |
|||
Loading…
Reference in new issue