不幸的是,似乎没有办法将 reviver 传递给 Angular HttpClient 中使用的 JSON.parse 方法。这是他们调用 JSON.parse 的源代码:
https ://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/xhr.ts#L189
如果响应类型设置为“json”,Angular 只会解析响应(您可以在第 183 行看到这一点)。如果您不指定响应类型,则 Angular 默认为“json”(https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/request.ts#L112)。
因此,您可以使用“文本”的响应类型并自己解析 json。或者你可能只是懒惰并序列化然后反序列化响应(JSON.parse(JSON.stringify(res.body), reviver)
)。
您可以创建一个拦截器,而不是修改每个调用,如下所示:
json-interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
// https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts#L18
const XSSI_PREFIX = /^\)\]\}',?\n/;
/**
* Provide custom json parsing capabilities for api requests.
* @export
* @class JsonInterceptor
*/
@Injectable()
export class JsonInterceptor implements HttpInterceptor {
/**
* Custom http request interceptor
* @public
* @param {HttpRequest<any>} req
* @param {HttpHandler} next
* @returns {Observable<HttpEvent<any>>}
* @memberof JsonInterceptor
*/
public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.responseType !== 'json') {
return next.handle(req);
}
// convert to responseType of text to skip angular parsing
req = req.clone({
responseType: 'text'
});
return next.handle(req).map(event => {
// Pass through everything except for the final response.
if (!(event instanceof HttpResponse)) {
return event;
}
return this.processJsonResponse(event);
});
}
/**
* Parse the json body using custom revivers.
* @private
* @param {HttpResponse<string>} res
* @returns{HttpResponse<any>}
* @memberof JsonInterceptor
*/
private processJsonResponse(res: HttpResponse<string>): HttpResponse<any> {
let body = res.body;
if (typeof body === 'string') {
const originalBody = body;
body = body.replace(XSSI_PREFIX, '');
try {
body = body !== '' ? JSON.parse(body, (key: any, value: any) => this.reviveUtcDate(key, value)) : null;
} catch (error) {
// match https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts#L221
throw new HttpErrorResponse({
error: { error, text: originalBody },
headers: res.headers,
status: res.status,
statusText: res.statusText,
url: res.url || undefined,
});
}
}
return res.clone({ body });
}
/**
* Detect a date string and convert it to a date object.
* @private
* @param {*} key json property key.
* @param {*} value json property value.
* @returns {*} original value or the parsed date.
* @memberof JsonInterceptor
*/
private reviveUtcDate(key: any, value: any): any {
if (typeof value !== 'string') {
return value;
}
if (value === '0001-01-01T00:00:00') {
return null;
}
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (!match) {
return value;
}
return new Date(value);
}
}
然后你必须在你的模块中提供它:
*.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { JsonInterceptor } from '...';
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: JsonInterceptor,
multi: true
}
]
})
...
在示例中,我尝试尽可能多地模仿 angular 是如何进行解析的。他们似乎没有导出 HttpJsonParseError 所以我无法将错误转换为该类型。它可能并不完美,但我希望它能传达这个想法。
这是一个正在运行的示例(在控制台中查看解析的日期):https ://stackblitz.com/edit/json-interceptor
我在这里创建了一个功能请求:
https ://github.com/angular/angular/issues/21079