I have an angular2 app which communicates with an ASP.Net MVC backend.
The backend requires an AntiForgeryToken
to be present on POST
requests.
For this, my index.html
is served with a hidden input where its value represents the token. I guess this is a common approach in an ASP.Net MVC
project (not topic of this question).
Now to grab this token and use it in my request headers i extended BaseRequestOptions
in my main.ts like this:
@Injectable()
class AntiForgeryRequestOptions extends BaseRequestOptions {
constructor () {
super();
let xsrfElement = <HTMLInputElement>document.querySelector('[name=__RequestVerificationToken]');
if (xsrfElement) {
let xsrfToken:string = xsrfElement.value;
this.headers.append('X-XSRF-Token', xsrfToken);
}
this.headers.append('Content-Type', 'application/json');
}
}
and provided it as RequestOptions
like this:
bootstrap(AppComponent, [
...
provide(RequestOptions, {useClass: AntiForgeryRequestOptions}),
...
]);
With this set up, i can make POST
-requests in my services like this:
post(url:string, body:any):Observable<any> {
let options = new RequestOptions();
return this._http.post(url, body, options)
.map((response:any) => {
...
})
.catch(this._handleError);
}
which works fine. The POST
works and the CSRF Token
is send with it.
Today i had to adjust this, because i have to switch out the Token on some occassions and experienced some weird behaviour i can't explain:
In my understanding as soon as i do this: let options = new RequestOptions()
the constructor of AntiForgeryRequestOptions
should have been called and headers should be set for this RequestOptions
i just creatd. But if i do a console.log(options)
directly after the call, i see that all properties are NULL
even the headers. (It still works, the CSRF Token
is send, its content-type
is application/json
) but i can't access it or change it for the coming request.
Can anyone tell me why this is happening or how i would alter my previously defined RequestOptions
in the constructor of my AntiForgeryRequestOptions
?
There was a reason why i did it that way. When i started this project, angular2 was still in beta state (now i'm working with angular2 rc1). At that time there was an issue with CSRF Tokens
where you can't set it directly in your options. Now with the new version i would not need to provide a custom AntiForgeryRequestOptions
. I could leave that away and just do it like this in the post requests:
post(url:string, body:any):Observable<any> {
let options = new RequestOptions();
options.headers = new Headers();
let xsrfElement = <HTMLInputElement>document.querySelector('[name=__RequestVerificationToken]');
if (xsrfElement) {
let xsrfToken:string = xsrfElement.value;
options.headers.append('X-XSRF-Token', xsrfToken);
}
options.headers.append('Content-Type', 'application/json');
...
}
As Günther suggested in his comment, i have to use merge
in my custom AntiForgeryRequestOptions
like this:
@Injectable()
class AntiForgeryRequestOptions extends BaseRequestOptions {
constructor () {
super();
}
merge(options?:RequestOptionsArgs):RequestOptions {
options.headers = new Headers();
let xsrfElement = <HTMLInputElement>document.querySelector('[name=__RequestVerificationToken]');
if (xsrfElement) {
let xsrfToken:string = xsrfElement.value;
options.headers.append('X-XSRF-Token', xsrfToken);
}
options.headers.append('Content-Type', 'application/json');
return super.merge(options);
}
}
which works. Now everytime i call new RequestOptions()
the Headers
property is updated correctly, which means whenever i change the value of my hidden input field for the CSRF Token, the headers will adjust like they should.
You need to implement a custom merge(options?: RequestOptionsArgs): RequestOptions { ... }
method because the default implementation just checks if headers were passed to merge(...)
and then takes these or otherwise takes the headers of the current AntiForgeryRequestOptions
instance but id doesn't actually merge.
Example (from the question - thanks for the permission)
@Injectable()
class AntiForgeryRequestOptions extends BaseRequestOptions {
constructor () {
super();
}
merge(options?:RequestOptionsArgs):RequestOptions {
options.headers = new Headers();
let xsrfElement = <HTMLInputElement>document.querySelector('[name=__RequestVerificationToken]');
if (xsrfElement) {
let xsrfToken:string = xsrfElement.value;
options.headers.append('X-XSRF-Token', xsrfToken);
}
options.headers.append('Content-Type', 'application/json');
return super.merge(options);
}
}