Angular2 App中扩展BaseRequestOptions的意外行为

时间:2016-06-22 12:46:06

标签: asp.net asp.net-mvc angular csrf-protection

我有一个angular2应用程序,它与ASP.Net MVC后端通信。 后端需要在AntiForgeryToken次请求中显示POST

为此,我的index.html提供了隐藏输入,其值代表令牌。我想这是ASP.Net MVC项目中的一种常见方法(不是这个问题的主题)。

现在抓住这个令牌并在我的请求标题中使用它我在我的main.ts中扩展BaseRequestOptions,如下所示:

@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');
    }
}

并将其提供为RequestOptions,如下所示:

bootstrap(AppComponent, [
    ...
    provide(RequestOptions, {useClass: AntiForgeryRequestOptions}),
    ...
]);

通过此设置,我可以在我的服务中提出POST - 请求:

post(url:string, body:any):Observable<any> {
        let options = new RequestOptions();

        return this._http.post(url, body, options)
            .map((response:any) => {
                ...
            })
            .catch(this._handleError);
    }

工作正常。 POST正常工作,CSRF Token随之发送。

问题

今天我不得不调整这一点,因为我必须在某些场合切换出令牌并经历一些我无法解释的奇怪行为:

我的理解是:let options = new RequestOptions()我应该调用AntiForgeryRequestOptions的构造函数,并为此RequestOptions创建标题。但是,如果我在通话后直接进行console.log(options),我会看到所有属性都是NULL甚至标题。 (它仍然有效,CSRF Token已发送,其content-typeapplication/json)但我无法访问它或更改即将发出的请求。

任何人都可以告诉我为什么会发生这种情况,或者我如何改变我RequestOptions的构造函数中之前定义的AntiForgeryRequestOptions

其他信息:

我有理由这样做。当我开始这个项目时,angular2仍处于beta状态(现在我正在使用angular2 rc1)。当时CSRF Tokens存在问题,您无法在选项中直接设置它。现在有了新版本,我不需要提供自定义AntiForgeryRequestOptions。我可以把它留下来,只是在帖子请求中这样做:

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');

        ...
    }

解决方案

正如Günther在评论中所说,我必须在我的自定义merge中使用AntiForgeryRequestOptions

@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);
    }
}

哪个有效。现在,每当我调用new RequestOptions()时,Headers属性都会正确更新,这意味着无论何时我更改CSRF令牌的隐藏输入字段的值,标题都会按照应有的方式进行调整。

1 个答案:

答案 0 :(得分:2)

您需要实现自定义merge(options?: RequestOptionsArgs): RequestOptions { ... }方法,因为默认实现只是检查标头是否传递给merge(...)然后采用这些或以其他方式获取当前AntiForgeryRequestOptions实例的标头但是id实际上并没有合并。

另见https://github.com/angular/angular/blob/f39c9c9e75671a7e235734b6b8aef263f6dff254/modules/%40angular/http/src/base_request_options.ts#L101

示例(来自问题 - 感谢您的许可)

@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);
    }
}

另见https://stackoverflow.com/a/37550368/217408