2017-10-04 32 views
7

Tôi hiện đang chuyển sang HttpClient of Angular 4.3 mới. Một lợi thế là tôi có thể chỉ định một loại thông tin trên phương thức GET và JSON được trả về được phân tích cú pháp thành loại đã cho, ví dụ:Ngày phân tích cú pháp với Angular 4.3 HttpClient

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

Nhưng thật không may, tất cả các ngày trong JSON được phân tích cú pháp dưới dạng số trong đối tượng kết quả (có thể vì các đối tượng Ngày Java được tuần tự hóa dưới dạng số trong phụ trợ).

Với Http cũ tôi đã sử dụng một hàm Reviver khi gọi JSON.parse() như thế này:

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

và trong hàm Reviver Tôi tạo ra đối tượng ngày từ những con số:

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

    return value; 
} 

Có một cơ chế tương tự với HttpClient mới không? Hoặc thực hành tốt nhất để thực hiện chuyển đổi khi JSON được phân tích cú pháp là gì?

+1

Sẽ không thể di chuyển chức năng này cho đối tượng bạn đang mong đợi, tức là Người? – Laurens

+0

Người có nhiều khả năng là một giao diện, vì vậy nó không có methos –

Trả lời

1

Bạn có thể sử dụng:

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

Đáng tiếc là có vẻ không phải là một cách để vượt qua một người bình phục với phương pháp JSON.parse được sử dụng trong phạm vi góc HttpClient. Đây là mã nguồn cho nơi họ gọi JSON.parse: https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/xhr.ts#L189

Góc sẽ chỉ phân tích phản hồi nếu kiểu phản hồi được đặt thành "json" (bạn có thể thấy điều này trên dòng 183). Nếu bạn không chỉ định kiểu phản hồi thì Angular sẽ mặc định là "json" (https://github.com/angular/angular/blob/20e1cc049fa632e88dfeb00476455a41b7f42338/packages/common/http/src/request.ts#L112).

Vì vậy, bạn có thể sử dụng loại phản hồi "văn bản" và phân tích cú pháp chính con cháu. Hoặc bạn chỉ có thể lười biếng và tuần tự hóa và sau đó deserialize phản ứng (JSON.parse(JSON.stringify(res.body), reviver)).

Thay vì thay đổi mỗi cuộc gọi bạn có thể tạo ra một đánh chặn như sau:

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); 
    } 
} 

Sau đó, bạn phải cung cấp cho nó trong module của bạn:

* .module.ts

import { HTTP_INTERCEPTORS } from '@angular/common/http'; 
import { JsonInterceptor } from '...'; 

@NgModule({ 
    providers: [ 
     { 
      provide: HTTP_INTERCEPTORS, 
      useClass: JsonInterceptor, 
      multi: true 
     } 
    ] 
}) 
... 

Trong ví dụ này, tôi đã cố gắng bắt chước cách góc độ phân tích cú pháp càng nhiều càng tốt. Họ dường như không xuất khẩu HttpJsonParseError vì vậy tôi không thể bỏ lỗi cho loại đó. Nó có lẽ không phải là hoàn hảo nhưng tôi hy vọng nó được ý tưởng trên.

Dưới đây là một ví dụ chạy (nhìn vào giao diện điều khiển để xem ngày phân tích cú pháp): https://stackblitz.com/edit/angular-qqbmcx

Tôi tạo ra một yêu cầu tính năng ở đây: https://github.com/angular/angular/issues/21079

+3

Tôi không thể tin rằng không có cách nào tốt hơn để triển khai JSON API với ngày/giờ. – Ryan

+0

Đã thăng hạng mọi thứ tôi có thể. Nó có thể giúp ai đó biết rằng máy đánh chặn này cần phải được đăng ký để làm việc. Liên kết này đã giúp tôi làm như vậy: https://medium.com/@ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8 – evilkos

+0

Tôi cũng thấy rằng sau khi đăng ký máy đánh chặn này, của tôi Trình xử lý lỗi toàn cục đã ngừng hiển thị thông báo lỗi từ tải trọng, vì các phản hồi lỗi hiện không được phân tích cú pháp. Để khắc phục điều này, tôi cần phải sửa đổi phần cuối của phương thức 'chặn' như vậy' trả về next.handle (req) .catch ((e, c) => {e.error = JSON.parse (e.error); Observable.throw (e);}) Bản đồ (event => {... 'Đó chỉ là một mẹo, có thể được cải thiện rõ ràng cũng làm sống lại những ngày ít nhất. – evilkos

0

Bạn vẫn có thể, nhưng bạn cần phải nhập map() -operator từ rxjs như thế này:

import 'rxjs/add/operator/map'; 

Sau đó, bạn có thể, cũng giống như Diego đã chỉ ra, sử dụng map như thế này:

return this.http.get<BlogPost>(url) 
.map(x => { 
x.published = new Date(String(x.published)); 
    return x; 
}) 
[...] 
Các vấn đề liên quan