In general web applications, it is often necessary to add header or some default parameters every time an Http request is sent. This article will take a look at several ways to implement this requirement. Through this implementation, we can also understand Angular's services and its principles of providers.
Our goal is to add a token to the header for each Http request to authenticate on the server side. Because Http is a service, I took it for granted that I could provide it through the extension frameworkHttp
To add. So how do we extend the services provided by a framework? That is to use providers.
existNgModule
There is an attribute inproviders
Generally, we use it to tell the framework that our app needs to use certain services we define, for example, I wrote aUserService
Used to read and write user data, for example, write aAuthGuardService
To implement routingGuard
. For frameworks or other component libraries used, we do not need to add them here, we only need toimports
Just add the corresponding module.
Custom system services
So, how can we implement it if we want to modify a service provided by the framework, such as if we want to extend it? We can add this service toproviders
In, the way it is added is different. The following method is required:
@NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, RouterModule, HttpModule ], providers: [UserService, AuthGuardService, { provide: Http, useClass: BaseHttp } ], bootstrap: [ AppComponent ] })
We've expandedHttp
The new service class name isBaseHttp
, and thenproviders
Used in{ provide: Http, useClass: BaseHttp }
, tell the framework that we want to useBaseHttp
This class provides the rightHttp
Implementation of . Then, the Http service inside the Angular container is actuallyBaseHttp
This class implementation, when we obtain aHttp
When it comes to examples, it is also obtainedBaseHttp
Examples of .
Implement automatic header addition
Next, let’s take a look at how to implement automatic header addition. First of all, the first way I think of is to extend Http and set a default header in its constructor.
Implemented in constructor
@Injectable() export class BaseHttp extends Http { constructor (backend: XHRBackend, options: RequestOptions) { super(backend, options); let token = (); (, token); } }
This is to get the token from localStorage in the constructor and put it in RequestOptions. It seems that there is no problem, but when running, it is found that the Http service was created when the app is initialized, so when this constructor is called, there may not be tokens in localStorage. In this way, even if the user logs in later, the previous default options will not be updated.
Implemented in request
Therefore, it is definitely not possible to implement it in the constructor. I saw that there are many methods by observing the interface of Http (through the IDE you use, you can track the interface definition file to view the interface definition).get(...), post(...), put(...)
Wait, if I need to reimplement all of these methods, it would be too troublesome and feel unnecessary. Then, I sawrequest(...)
Method, look at the comments of his method, know that all other methods will eventually call this method to send the actual request. So, we just need to rewrite this method:
@Injectable() export class BaseHttp extends Http { constructor (backend: XHRBackend, options: RequestOptions) { super(backend, options) } request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> { const token = () if (typeof url === 'string') { // meaning we have to add the token to the options, not in url if (!options) { options = new RequestOptions({}) } (, token) } else { (, token) } return (url, options) } }
This implementation is also easy. The only thing to be noted is that there may be two types of url here, string or Request. If it is a string type, it means that this url is a string, and the header we want to set will definitely not be inside it.
If url isRequest
What about the type? Let's take a look at its definition. By looking at its definition:
export declare class Request extends Body { /** * Http method with which to perform the request. */ method: RequestMethod; /** * {@link Headers} instance */ headers: Headers; /** Url of the remote resource */ url: string; /** Type of the request body **/ private contentType; /** Enable use credentials */ withCredentials: boolean; /** Buffer to store the response */ responseType: ResponseContentType; constructor(requestOptions: RequestArgs); /** * Returns the content type enum based on header options. */ detectContentType(): ContentType; /** * Returns the content type of request's body based on its type. */ detectContentTypeFromBody(): ContentType; /** * Returns the request's body according to its type. If body is undefined, return * null. */ getBody(): any; }
We know it is a class with a member inside itheaders
, and then let's take a lookHeaders
This type, when you see that it has a set() method, it is used to add values to headers, which is exactly what we need.
So, in our implementation()
In the method, based on the type of url, then determine whether options are empty or not. Through testing, this method can achieve our needs. Whether it is the token in localStorage during initialization, logging in later, or even logging out after logging in (the token of localStorage will be updated), etc., it can be met.
Reimplement RequestOptions
Although the above method and can solve the problem, can it be simpler? Because all we need is to update Options, however, for this, we intercepted the Http request. Can we directly expandRequestOptions
What to do? The answer is yes. And it's easier, we can inherit itBaseRequestOptions
, rewritemerge(...)
method.
@Injectable() export class AuthRequestOptions extends BaseRequestOptions { merge(options?: RequestOptionsArgs): RequestOptions { let newOptions = (options); let token = (); (, token); return newOptions; } }
thismerge(...)
The method will be called every time the request is used to merge the options and default options during the request.
After testing, this method can also perfectly solve our needs.
Summarize
Therefore, this is the power and convenience of Angular. It uses many characteristics of phenomenal objects, such as inheritance, interface, implementation, etc.; it also uses many characteristics of server-side Java frameworks, such as containers, etc. What's said aboveprovider
That is, the provider of object instances in the container, originallyRequestOptions
The type of provider isBaseRequestOptions
, however, I inherited it, rewritten a method, and changed the provider of this type to the class I wrote. In this way, when the Angular container is initialized, it will use the class I provide to create an instance of this type.
Moreover, during the exploration of these implementation methods, I did not check Angular's documentation at all, nor did I check any information online. Knowledge to view the definition of a class or interface, through its annotations, I have an idea, and then try to implement it, and it succeeds. This is also the traversal brought to me by TypeScript.
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.