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, allreturn
So, 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<any>) => { 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<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> { ('interceptor') const jwtReq = ({ headers: ('token', 'asdf') }); return next .handle(jwtReq) .mergeMap((event: any) => { if (event instanceof HttpResponse && !== 0) { return (observer => (event)); } return (observer => (event)); }) .catch((res: HttpResponse<any>) => { 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?
<div *ngIf="user | async as user; else loading"> {{ user | json }} </div> <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.