Angular2 App 中扩展 BaseRequestOptions 的意外行为

Unexpected behaviour with extended BaseRequestOptions in Angular2 App

我有一个与 ASP.Net MVC 后端通信的 angular2 应用程序。 后端要求 AntiForgeryToken 出现在 POST 请求中。

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

现在获取此令牌并在我的请求中使用它 headers 我在我的 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 的构造函数应该被调用并且 headers 应该为此设置 RequestOptions 我只是创建。但是,如果我在调用后直接执行 console.log(options),我会看到所有属性都是 NULL,甚至是 headers。 (它仍然有效,CSRF Token 已发送,它的 content-typeapplication/json)但我无法访问它或为即将到来的请求更改它。

谁能告诉我为什么会这样,或者我将如何更改我之前在 AntiForgeryRequestOptions 的构造函数中定义的 RequestOptions


附加信息:

我这样做是有原因的。当我开始这个项目时,angular2 仍处于 beta 状态(现在我正在使用 angular2 rc1)。当时 CSRF Tokens 有一个问题,你不能直接在你的选项中设置它。现在有了新版本,我不需要提供自定义 AntiForgeryRequestOptions。我可以把它放在一边,然后在 post 请求中这样做:

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 在他的评论中建议的那样,我必须在我的自定义 AntiForgeryRequestOptions 中使用 merge,如下所示:

@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 令牌的隐藏输入字段的值时,headers 都会调整为他们应该。

您需要实现自定义 merge(options?: RequestOptionsArgs): RequestOptions { ... } 方法,因为默认实现只检查 headers 是否传递给 merge(...) 然后获取这些方法或以其他方式获取 headers当前 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://whosebug.com/a/37550368/217408