2017-10-04 68 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; 
}) 
[...]