2017-10-04 67 views
7

我目前正在切换到Angular 4.3的新HttpClient。一个优点是我可以在GET方法中指定一个类型信息,并且返回的JSON被解析为给定的类型,例如,与Angular 4.3的解析日期HttpClient

this.http.get<Person> (url).subscribe(...) 

但不幸的是,在所有JSON日期解析为在结果对象编号(可能是因为Java日期对象序列化为数字在后端)。

随着旧的Http我喜欢这个调用JSON.parse()来时使用的齐磊功能:

this.http.get(url) 
    .map(response => JSON.parse(response.text(), this.reviver)) 

,并在齐磊功能我创建从数字日期对象:

reviver (key, value): any { 
    if (value !== null && (key === 'created' || key === 'modified')) 
    return new Date(value); 

    return value; 
} 

是否有与新的HttpClient类似的机制?或者在解析JSON时进行转换的最佳做法是什么?

+1

难道不可能将这个reviver函数重定位到您期望的对象,即Person? – Laurens

+0

人最有可能是一个接口,所以它没有方法 –

回答

1

您可以使用:

this.http.get(url, { responseType: 'text' }) 
    .map(r => JSON.parse(r, this.reviver)) 
    .subscribe(...) 
3

那里似乎不幸的是不要被一个齐磊传递给所内的角度HttpClient的使用JSON.parse方法的一种方式。下面是他们叫其中JSON.parse源代码: https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/xhr.ts#L189

角如果响应类型设置为“JSON”(你可以看到这个上线183)将只解析响应。如果您未指定响应类型,则Angular默认为“json”(https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/request.ts#L112)。

所以你可以使用“text”的响应类型并自己解析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 
     } 
    ] 
}) 
... 

在这个例子中,我试图模仿角度如何尽可能地进行解析。它们似乎不会导出HttpJsonParseError,因此我无法将错误转换为该类型。这可能不是完美的,但我希望它能得到这个想法。

这里是一个正在运行的例子(看在控制台中看到的日期解析到):https://stackblitz.com/edit/angular-qqbmcx

我创建了一个功能要求在这里: https://github.com/angular/angular/issues/21079

+3

我不相信有没有更好的方式来实现JSON API的日期/时间。 – Ryan

+0

Upvoted我可以。它可以帮助别人知道这个拦截器需要注册才能工作。这个链接帮助我这么做:https://medium.com/@ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8 – evilkos

+0

我也发现在注册这个拦截器之后,我的全局错误处理程序停止显示来自有效内容的错误消息,因为错误响应现在不被解析。为了解决这个问题,我需要修改'intercept'方法的结尾,就像'return next.handle(req).catch((e,c)=> {e.error = JSON.parse(e.error); return Observable.throw(e);})。map(event => {...'这只是一个提示,可以明显改善,至少可以恢复日期 – evilkos

0

你仍然可以,但你需要导入map() - 运算符这样从rxjs:

import 'rxjs/add/operator/map'; 

然后你就可以,就像迭戈指出,使用map这样的:

return this.http.get<BlogPost>(url) 
.map(x => { 
x.published = new Date(String(x.published)); 
    return x; 
}) 
[...]