Json 使用Angular 4.3 HttpClient解析日期
我目前正在切换到Angular 4.3的新HttpClient。一个优点是我可以在GET方法上指定一个类型信息,并且返回的JSON被解析为给定的类型,例如Json 使用Angular 4.3 HttpClient解析日期,json,angular,date,angular-httpclient,Json,Angular,Date,Angular Httpclient,我目前正在切换到Angular 4.3的新HttpClient。一个优点是我可以在GET方法上指定一个类型信息,并且返回的JSON被解析为给定的类型,例如 this.http.get<Person> (url).subscribe(...) 在reviver函数中,我根据数字创建了日期对象: reviver (key, value): any { if (value !== null && (key === 'created' || key === 'modif
this.http.get<Person> (url).subscribe(...)
在reviver函数中,我根据数字创建了日期对象:
reviver (key, value): any {
if (value !== null && (key === 'created' || key === 'modified'))
return new Date(value);
return value;
}
新的HttpClient有类似的机制吗?或者,解析JSON时进行转换的最佳实践是什么?您可以使用:
this.http.get(url, { responseType: 'text' })
.map(r => JSON.parse(r, this.reviver))
.subscribe(...)
rxjs 6+的更新
this.http.get(url, { responseType: 'text' })
.pipe(map(r => JSON.parse(r, this.reviver)))
.subscribe(...)
不幸的是,似乎没有办法将恢复程序传递给HttpClient中使用的JSON.parse方法。下面是他们调用JSON.parse的源代码: Angular将只在响应类型设置为“json”时解析响应(您可以在第183行看到这一点)。如果不指定响应类型,则默认为“json”() 因此,您可以使用响应类型“text”并自己解析json。或者,您可以只是懒惰地序列化然后反序列化响应(
JSON.parse(JSON.stringify(res.body),reviver)
)
您可以创建如下拦截器,而不是修改每个调用:
json拦截器.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);
}
}
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { JsonInterceptor } from '...';
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: JsonInterceptor,
multi: true
}
]
})
...
在这个例子中,我试图尽可能多地模仿angular在进行解析。它们似乎没有导出HttpJsonParseError,因此我无法将错误转换为该类型。它可能并不完美,但我希望它能让人明白这一点
下面是一个正在运行的示例(查看控制台以查看解析的日期):
我在这里创建了一个功能请求:
您仍然可以,但是您需要从rxjs导入
map()
-操作符,如下所示:
this.http.get(url)
.map(response => JSON.parse(response.text(), this.reviver))
import 'rxjs/add/operator/map';
return this.http.get<BlogPost>(url)
.map(x => {
x.published = new Date(String(x.published));
return x;
})
[...]
然后,正如迭戈指出的那样,你可以像这样使用地图
:
this.http.get(url)
.map(response => JSON.parse(response.text(), this.reviver))
import 'rxjs/add/operator/map';
return this.http.get<BlogPost>(url)
.map(x => {
x.published = new Date(String(x.published));
return x;
})
[...]
返回此.http.get(url)
.map(x=>{
x、 发布=新日期(字符串(x.published));
返回x;
})
[...]
这对我很有用:
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class ApiInterceptor implements HttpInterceptor {
private dateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)$/;
private utcDateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
this.convertDates(event.body);
}
});
}
private convertDates(object: Object) {
if (!object || !(object instanceof Object)) {
return;
}
if (object instanceof Array) {
for (const item of object) {
this.convertDates(item);
}
}
for (const key of Object.keys(object)) {
const value = object[key];
if (value instanceof Array) {
for (const item of value) {
this.convertDates(item);
}
}
if (value instanceof Object) {
this.convertDates(value);
}
if (typeof value === 'string' && this.dateRegex.test(value)) {
object[key] = new Date(value);
}
}
}
}
从'@angular/core'导入{Injectable};
从'@angular/common/http'导入{HttpInterceptor,HttpRequest,HttpHandler,HttpEvent,HttpErrorResponse,HttpResponse};
从“rxjs/Observable”导入{Observable};
@可注射()
导出类ApiInterceptor实现HttpInterceptor{
私有dateRegex=/^(\d{4})-(d{2})-(d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)$/;
私有utcDateRegex=/^(\d{4})-(d{2})-(d{2})T(\d{2}):(\d{2}):(\d{2}:(\d{2}:(\.\d*)?)Z$/;
构造函数(){}
intercept(请求:HttpRequest是指您不需要自己解析为json,只需在解析完成后转换日期即可
这也适用于数组和嵌套对象。
我修改了它,它应该支持阵列。类似于,但使用管道:
import { map } from "rxjs/operators";
this.http.get(url)
.pipe(
map(response => {
response.mydate = new Date(response.mydate);
return response;
})
请注意map
运算符的不同导入语法
管道是在RxJS 5.5中引入的。它们有助于导入处理、代码可读性和减少捆绑包大小。请参阅。是否可以将此恢复器函数重新定位到您期望的对象,即Person?Person很可能是一个接口,因此它没有方法。我不相信没有更好的方法用d实现JSON APIate/times.Up尽我所能投票。这可能有助于某人知道此拦截器需要注册才能工作。此链接帮助我这样做:我还发现,注册此拦截器后,我的全局错误处理程序停止显示来自有效负载的错误消息,因为错误响应现在未被解析。要解决此问题,我需要o修改intercept
方法的结尾,就像这样return next.handle(req).catch((e,c)=>{e.error=JSON.parse(e.error);return Observable.throw(e);}).map(事件=>{…
这只是一个提示,显然可以改进,至少可以恢复日期。@evilkos很好。我添加了一个注册拦截器的示例。@evilkos你是说HttpClient正在将错误解析为json吗?我在他们的OneError中没有看到任何可以这样做的内容。负载可能是无效的json。所以你可能需要在全局错误处理程序中处理此问题。我添加了一个没有“T”的新模式,它对我有效:)我得到错误属性“map”在类型“Observable”上不存在。
由于rxjs的新版本,您必须使用管道()
操作符。