SoFunction
Updated on 2025-04-04

Detailed explanation of how to write HTTP requests elegantly in Angular

introduction

Basically, current applications will be divided into front-end and back-end. Of course, this kind of front-end definition is not limited to desktop browsers, mobile phones, APPs and other devices. A good backend will communicate between the front and backends through a set of RESTful API sequence interfaces that are common to all frontends.

In this way, it is impossible to rely on traditional sessions or cookies for identity authentication; instead, authentication methods such as OAuth2 and JWT are more suitable for API interfaces. Of course this article does not discuss how to build them.

1. API design

First of all, although the technology of identity authentication will not be discussed, whether it is OAuth2 or JWT, identity authentication is essentially maintained by one token; therefore, the value required for identity authentication is uniformly represented by tokens.

A reasonable set of API rules will make front-end encoding more elegant. Therefore, it is also necessary to reach a "protocol" with the backend before writing Angular. You can try to consider it from the following points.

Version number

Can be found in the URL (for example:/v1/) or Header (example:headers: { version: 'v1' } ) is reflected in, and I prefer the directness of the former.

Business nodes

A node is used to represent a certain business, such as:

  • Product /v1/product/
  • Product SKU /v1/product/sku/

action

Expressed by HTTP verbs:

  • GET requests a product /product/${ID}
  • POST Create a new product /product
  • PUT Modify a product /product/${ID}
  • DELETE Delete a product /product/${ID}

Unified response

This is very important, especially when we create a new product, the product has many attributes, but if we lack a certain attribute. A unified response format can be used:

{
  "code": 100, // 0 means success  "errors": { // Error details    "title": "Product name required"
  }
}

incode This attribute will be available regardless of success or not.

Status code

The backend responds to a request to include status code and response content, and each status code contains a different meaning.

  • 200 Successfully returned requested data
  • 401 No permission
  • 404 Invalid resource

2. How to access Http?

First, you need to import the HttpClientModule module.

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule
  ]
})

Then, inject HttpClient into the component class.

export class IndexComponent {
  constructor(private http: HttpClient) { }
}

Finally, the request clicks a button to send a GET request.

user: Observable<User>;
getUser() {
   = <User>('/assets/data/');
}

Print result:

{{ user | async | json }}

Three simple steps are a complete HTTP request step.

Then, there is some distance between reality and reality, such as identity authentication, error processing, status code processing and other issues, which do not reflect anything on it.

But, the above is already elegant enough. If I want me to destroy this elegance, then this article will become meaningless!

therefore……

III. Interceptor

1. HttpInterceptor interface

As the name suggests, we allow us to solve the problems of identity authentication, error handling, and status code handling without changing the above application-level code!

Writing an interceptor is also very elegant, only requires implementationHttpInterceptor Just interface, and only oneintercept method.

@Injectable()
export class JWTInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
    // doing
  }

}

intercept The method has two parameters, it is almost the most popular middleware concept.req Indicates the current requested data (including: url, parameters, header, etc.),next Indicates calling the next "middleware".

2. Identity authentication

req There is oneclone Methods allow cloning of the current request parameters and this process will deduce them based on some parameters, regardless of how it is used to generate a new request data and add the data we expect to in this new data, such as: token.

const jwtReq = ({
  headers: ('token', 'xxxxxxxxxxxxxxxxxxxxx')
});

Of course, you can toss more configurations before requesting.

Finally, pass the new request parameter to the next "middleware".

return (jwtReq);

Wait, allreturnSo, what about the promised status code and exception handling?

3. Exception handling

Look carefully Returns is aObservable type. SeeObservable What will happen to us?mergeMap、catch Wait for a lot of things.

Therefore, we can use these operators to change the value of the response.

mergeMap

There will be some process status during the request process, such as before request, upload progress bar, end of request, etc. Angular will touch next every time this type of action. Therefore, we only need to returnObservable Object plusmergeMap Let’s observe the changes in these values, so that there is a very large free space to imagine.

return (jwtReq).mergeMap((event: any) => {
    if (event instanceof HttpResponse &&  !== 0) {
      return (observer => (event));
    }
    return (observer => (event));
  })

A HttpResponse type will only be returned when the request is successful. Therefore, we can boldly judge whether it comes from HttpResponse to indicate that the HTTP request has been successful.

Here, the unified business-level error code !== 0 generates an Observable with an error signal. Instead, generate a successful message.

catch

catch to catch errors in status codes other than 200, such as: 401. At the same time, the error signal generated by the previous mergeMap will also be captured here.

.catch((res: HttpResponse&lt;any&gt;) =&gt; {
  switch () {
    case 401:
      // Permission processing       = ''; // Log in again      break;
    case 200:
      // Business level error handling      alert('Business Error:' + );
      break;
    case 404:
      alert('API does not exist');
      break;
  }
  return (res);
})

4. Complete code

At this point, the identity authentication token, unified response processing, and exception processing that the interceptor needs to include are all solved.

@Injectable()
export class JWTInterceptor implements HttpInterceptor {

  constructor(private notifySrv: NotifyService) {}

  intercept(req: HttpRequest&lt;any&gt;, next: HttpHandler): Observable&lt;HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse&lt;any&gt; | HttpUserEvent&lt;any&gt;&gt; {
    ('interceptor')
    const jwtReq = ({
      headers: ('token', 'asdf')
    });
    return next
      .handle(jwtReq)
      .mergeMap((event: any) =&gt; {
        if (event instanceof HttpResponse &amp;&amp;  !== 0) {
          return (observer =&gt; (event));
        }
        return (observer =&gt; (event));
      })
      .catch((res: HttpResponse&lt;any&gt;) =&gt; {
        switch () {
          case 401:
            // Permission processing             = ''; // Log in again            break;
          case 200:
            // Business level error handling            ('Business Error', `The error code is:${}`);
            break;
          case 404:
            ('404', `APIDoes not exist`);
            break;
        }
        // End this request in an incorrect form        return (res);
      })
  }
}

Didn't we find that we didn't add a lot of things we didn't know, we just just operate on data streams.

NotifyServiceIt is a minimalist Angular notification component that does not require dependency on HTML templates.

5. Register an interceptor

After the interceptor is built, it also needs to be registered in the HTTP_INTERCEPTORS identifier.

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: JWTInterceptor, multi: true}
  ]
})

The above is all the contents of the interceptor. Without changing the original code, we only use a few lines of code to achieve the TOKEN, business-level unified response processing, and error processing actions required for identity authentication.

4. async pipeline

oneObservable You must be subscribed before you can really start the action. We used it in the HTML template beforeasync The pipeline simplifies this subscription process.

{{ user | async | json }}

It is equivalent to:

let user: User;
get() {
  <User>('/assets/data/').subscribe(res => {
     = res;
  });
}
{{ user | json }}

However, async's simplification does not mean losing some degrees of freedom. For example, what should I do when [Loading...] is displayed during the data acquisition process?

&lt;div *ngIf="user | async as user; else loading"&gt;
  {{ user | json }}
&lt;/div&gt;
&lt;ng-template #loading>Loading...</ng-template>

kindness!

V. Conclusion

Angular uses Observable asynchronous data flow to control data during HTTP requests, and uses a large number of operators provided by rxjs to change the final value; thus obtaining the most elegant encoding style at the application level.

When we talk about using HTTP elegantly, easy testing is a very important thing, so I recommend stripping HTTP from the component class and putting all requests into the Service. When writing test code for a component, it is more difficult to test if it is restricted by the results of HTTP requests.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.