Browse Source

Update POST.md

pull/18989/head
Masum ULU 3 years ago
committed by GitHub
parent
commit
2239440f68
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 120
      docs/en/Community-Articles/2024-02-12-Global-Error-Handling-in-Angular/POST.md

120
docs/en/Community-Articles/2024-02-12-Global-Error-Handling-in-Angular/POST.md

@ -1,27 +1,21 @@
# Global Error Handling in Angular # Global Error Handling in Angular
### Error handling
![Error Handling](error_handling.jpg) ![Error Handling](error_handling.jpg)
- Error handling is how we deal with errors that go wrong when we are running a program. Error handling is how we deal with errors that go wrong when we are running a program. There is no code that runs perfectly forever :) Things can go wrong and your application might be crush. **So, in order to run your program smoothly you must handle errors.** It just not for to keep your application in running state. It is also usefull to show messages about the error to client. Like what went wrong, why it is not allowed to access this page etc.
- There is no code that runs perfectly forever :) Things can go wrong and your application might be crush. **So, in order to run your program smoothly you must handle errors.**
- It just not for to keep your application in running state. It is also usefull to show messages about the error to client. Like what went wrong, why it is not allowed to access this page etc.
### How to handle errors ### How to handle errors
- First of all you have to catch them 😀. You can catch them via **try-catch block**. See an example; - First of all you have to catch them 😀. You can catch them via **try-catch block**. See an example;
- Create an basic error; #### Create an basic error
```ts ```ts
({} as any).doSomething() ({} as any).doSomething()
``` ```
![Error Image](error-image-1.png) ![Error Image](error-image-1.png)
- **handle it;** #### Handle it
```ts ```ts
try { try {
({} as any).doSomething(); ({} as any).doSomething();
@ -36,7 +30,7 @@ try {
- In this case we know where the error will be throwed. Most of the time we wont know where the error will be appear. Should we cover entire application with try-catch blocks? of course not 😀 - In this case we know where the error will be throwed. Most of the time we wont know where the error will be appear. Should we cover entire application with try-catch blocks? of course not 😀
- **We are going to handle errors globally**. Angular provides a great way to do it. Lets do it step by step; - **We are going to handle errors globally**. Angular provides a great way to do it. Lets do it step by step;
1. Create an **service** and implements the **`ErrorHandler`** interface. ### 1.Create an **service** and implements the **`ErrorHandler`** interface.
```ts ```ts
import { ErrorHandler, Injectable, inject } from '@angular/core'; import { ErrorHandler, Injectable, inject } from '@angular/core';
@ -48,7 +42,7 @@ import { ToastService } from './toast.service';
export class CustomErrorHandlerService implements ErrorHandler { export class CustomErrorHandlerService implements ErrorHandler {
toastService = inject(ToastService); toastService = inject(ToastService);
// handleError method is comes from interface //This method is comes from interface
handleError(error: any): void { handleError(error: any): void {
this.toastService.showError(error.message); this.toastService.showError(error.message);
} }
@ -56,72 +50,63 @@ export class CustomErrorHandlerService implements ErrorHandler {
``` ```
2. Provide the service by using **`ErrorHandler`** class from **`@angular/core`**. ### 2.Provide the service by using **`ErrorHandler`** class from **`@angular/core`**.
```tsx ```ts
import { ErrorHandler } from '@angular/core'; import { ErrorHandler } from '@angular/core';
providers: [ providers: [
..., { provide: ErrorHandler, useExisting: CustomErrorHandlerService }
{ provide: ErrorHandler, useExisting: CustomErrorHandlerService }
] ]
``` ```
- Let’s remove try-catch block and see it in action.
![toast-gif](show-toast-2.gif) ![toast-gif](show-toast-2.gif)
- It behaves the exactly the same. Nice, now we catch the entire errors in one simple service. - It behaves the exactly the same. Nice, now we catch the entire errors in one simple service.
- Is it that simple? I wish it is but it's not 😀. This handling mechanism is only works synchronously. When we start to making http requests, our **`CustomErrorHandlerService`** wont catch the errors. - Is it that simple? I wish it is but it's not 😀. This handling mechanism is only works synchronously. When we start to making http requests, our **`CustomErrorHandlerService`** wont catch the errors.
## How to handle HTTP Requests ## How to handle HTTP Requests
Make an http request and check is it working.
- Make an http request and check is it working.
![http-request](http-request-4.gif)
- As you can see it doesn’t work. So how can we catch the http errors? with **`catchError()`** operator in rxjs or **`observer object`**. I will go with **`catchError()`** operator. ![http-request](http-request-4.gif)
As you can see it doesn’t work. So how can we catch the http errors? with **`catchError()`** operator in rxjs or **`observer object`**. I will go with **`catchError()`** operator.
```ts ```ts
getTodo() { getTodo(id: number) {
this.http.get('<https://jsonplaceholder.typicode.com/todos/1111>') this.http
.pipe(catchError((err) => { .get(`https://jsonplaceholder.typicode.com/todos/${id}`)
this.toastService.showError(err.message); .pipe(catchError((err) => {
return EMPTY; this.toastService.showError(err.message);
})) return EMPTY;
.subscribe(todo => { })
this.todo = todo; )
}); .subscribe(todo => this.todo = todo);
} }
``` ```
![http-request](http-request-5.gif) ![http-request](http-request-5.gif)
- So are we going to add this **`catchError()`** operator to the entire http requests? **NO, we will use HTTP Interceptors!** - So are we going to add this **`catchError()`** operator to the entire http requests? **NO, we will use HTTP Interceptors!**
- Lets do it step by step. - Lets do it step by step.
1. **Remove catchError pipe** ### 1.Remove catchError pipe
```tsx ```tsx
// Remove catchError pipe getTodo(id: number) {
getTodo() { this.http.get('https://jsonplaceholder.typicode.com/todos/${id}').subscribe(todo => this.todo = todo);
this.http.get('<https://jsonplaceholder.typicode.com/todos/1111>').subscribe(todo => {
this.todo = todo;
});
} }
``` ```
2. **Create an Http Interceptor.** ### 2.Create an Http Interceptor
```ts ```ts
import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable, inject } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { ToastService } from './toast.service'; import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { EMPTY, catchError } from 'rxjs'; import { EMPTY, catchError } from 'rxjs';
import { ToastService } from './toast.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -136,41 +121,33 @@ export class ToastInterceptor implements HttpInterceptor {
})); }));
} }
} }
``` ```
3. **Provide the interceptor** ### 3.Provide the interceptor
```ts ```ts
import { HTTP_INTERCEPTORS} from '@angular/common/http'; import { HTTP_INTERCEPTORS} from '@angular/common/http';
providers: [ providers: [
..., { provide: HTTP_INTERCEPTORS, useExisting: ToastInterceptor, multi: true }
{ provide: HTTP_INTERCEPTORS, useExisting: ToastInterceptor, multi: true} ]
]
``` ```
Now everything has setted up. lets make an http request and try.
- Now everything has setted up. lets make an http request and try.
![http-request](http-request-5.gif) ![http-request](http-request-5.gif)
- So, now we are handling http errors globally. Whenever an error occurs, it will be catched from interceptor and will be showed via toast message. - So, now we are handling http errors globally. Whenever an error occurs, it will be catched from interceptor and will be showed via toast message.
- But this method has a one little disadvantage. What if we dont want to show toast message for a spesific case? [related issue about this problem](https://github.com/angular/angular/issues/18155).
- But this method has a one little disadvantage. What if we dont want to show toast message for a spesific case? [related issue about this problem](https://github.com/angular/angular/issues/20203). - **[ABP Framework](https://abp.io/) has great solution for this problem. Let's understand the solution and apply it straightforwardly.**
- Create a singleton service called **`HttpErrorReporterService`.** This service is going to store **HttpError** in a subject, and share the httpError as an observable for subscribers.
- **[ABP Framework](https://abp.io/) has great solution for this problem. Let's understand the solution and apply it straightforwardly.** - Create a new service called **`RestService`** which is a layer on top of **`HttpClient`**, this new service is able to get a skipHandleError parameter. If skipHandleError value is **false** then it will be reported to the **`HttpErrorReporterService`** otherwise error will be throwed.
- Create a service called **`ErrorHandler`**, This service is going to subscribe to observable in **`HttpErrorReporterService`** and handle the errors (in our case we will show toast message).
- Create a singleton service called **`HttpErrorReporterService`.** This service is going to store **HttpError** in a subject, and share the httpError as an observable for subscribers. - When i first see this solution, i loved it. For more information and detail you can check the source code from the links below;
- Create a new service called **`RestService`** which is a layer on top of **`HttpClient`**, this new service is able to get a skipHandleError parameter. If skipHandleError value is **false** then it will be reported to the **`HttpErrorReporterService`** otherwise error will be throwed. - [**Http Error Reporter Service**](https://github.com/abpframework/abp/blob/360a3395aa0e44fb77574ea7eac745e8ba94b82e/npm/ng-packs/packages/core/src/lib/services/http-error-reporter.service.ts#L6)
- Create a service called **`ErrorHandler`**, This service is going to subscribe to observable in **`HttpErrorReporterService`** and handle the errors (in our case we will show toast message). - [**Rest Service**](https://github.com/abpframework/abp/blob/360a3395aa0e44fb77574ea7eac745e8ba94b82e/npm/ng-packs/packages/core/src/lib/services/rest.service.ts#L4)
- When i first see this solution, i loved it. For more information and detail you can check the source code from the links below; - [**Error Handler**](https://github.com/abpframework/abp/blob/360a3395aa0e44fb77574ea7eac745e8ba94b82e/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts#L19)
- [**Http Error Reporter Service**](https://github.com/abpframework/abp/blob/360a3395aa0e44fb77574ea7eac745e8ba94b82e/npm/ng-packs/packages/core/src/lib/services/http-error-reporter.service.ts#L6)
- [**Rest Service**](https://github.com/abpframework/abp/blob/360a3395aa0e44fb77574ea7eac745e8ba94b82e/npm/ng-packs/packages/core/src/lib/services/rest.service.ts#L4)
- [**Error Handler**](https://github.com/abpframework/abp/blob/360a3395aa0e44fb77574ea7eac745e8ba94b82e/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts#L19)
You can copy the source codes from ABP or use ABP directly 😀 You can copy the source codes from ABP or use ABP directly 😀
Lets simulate the solution in our application, this simulation is not suitable for your application it just for demonstration. Lets simulate the solution in our application, this simulation is not suitable for your application it just for demonstration.
**Rest Service** **Rest Service**
@ -188,7 +165,7 @@ export class RestService {
http = inject(HttpClient); http = inject(HttpClient);
httpErrorReporterService = inject(HttpErrorReporterService); httpErrorReporterService = inject(HttpErrorReporterService);
request(req: HttpRequest<any> | { method: string, url: string; }, skipHandleError: boolean = false) { request(req: HttpRequest<any> | { method: string, url: string; }, skipHandleError = false) {
const { method, url } = req; const { method, url } = req;
return this.http.request(method, url).pipe(catchError((err) => { return this.http.request(method, url).pipe(catchError((err) => {
if (!skipHandleError) { if (!skipHandleError) {
@ -255,12 +232,11 @@ restService = inject(RestService);
getTodo() { getTodo() {
this.restService.request( this.restService.request(
{ method: 'GET', url: '<https://jsonplaceholder.typicode.com/todos/1111>' }, { method: 'GET', url: 'https://jsonplaceholder.typicode.com/todos/1111' },
).subscribe(todo => { ).subscribe(todo => {
this.todo = todo; this.todo = todo;
}); });
} }
``` ```
![http-request](http-request-7.gif) ![http-request](http-request-7.gif)
@ -273,7 +249,7 @@ restService = inject(RestService);
getTodo() { getTodo() {
this.restService.request( this.restService.request(
{ method: 'GET', url: '<https://jsonplaceholder.typicode.com/todos/1111>' }, { method: 'GET', url: '<https://jsonplaceholder.typicode.com/todos/1111>' },
true skipHandleError: true,
).subscribe(todo => { ).subscribe(todo => {
this.todo = todo; this.todo = todo;
}); });
@ -283,4 +259,4 @@ getTodo() {
![http-request](http-request-8.gif) ![http-request](http-request-8.gif)
Thanks for reading, if you have any advice please share with me in the comments. Thanks for reading, if you have any advice please share with me in the comments.

Loading…
Cancel
Save