现在是凌晨2点半。

我从晚上8点就遇到这个问题了,一直想不通,直到刚才查到https://github.com/angular/angular/issues/18586才发现被坑了一回。

问题是这样的,使用Angular的HttpClient发出get请求。文档规定的get方法有2个参数,一个是URL字符串,另外一个是options。在options中可以指定headers、observe、params、reportProgress、responseType、withCredentials。

很自然地就会想到如下方式:把options单独提出来定义

    const uri = `${this.config.uri}/${this.domain}`;const httpOptions = {headers: this.headers,responseType: 'json',params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, httpOptions);

可以指定后台返回的数据格式(responseType),比如json格式,text格式,还有blob格式及arraybuffer格式。

如上这种方式看起来是很正常的操作,不过烦人的是tslint报了一个error:

类型“{ headers: HttpHeaders; responseType: string; params: HttpParams; }”的参数不能赋给类型“{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: HttpParams | { [param: string]: string | string[]; }; reportProgress?: boolean; responseType?: "json"; withCredentials?: boolean; }”的参数。
属性“responseType”的类型不兼容。
不能将类型“string”分配给类型“"json"”。 [2345]

很奇怪对不对?我也很奇怪,明明是正常操作,为什么会报error呢?get方法是支持把responseType设置成json的啊。

百思不得其解,点击get进入angular的代码,显示:

    get(url: string, options: {headers?: HttpHeaders | {[header: string]: string | string[];};observe?: 'body';params?: HttpParams | {[param: string]: string | string[];};reportProgress?: boolean;responseType: 'arraybuffer';withCredentials?: boolean;}): Observable<ArrayBuffer>;

这个更奇怪了,我明明定义的是json,为什么angular的get方法重载(get方法有15种重载)到arraybuffer呢?观察了好久猜测导航到的重载正好是15种重载中的第一个,是不是ts不能识别这个时候的重载规则所以默认导航到第一个重载了?

按照这个思路想下去,为什么ts不能识别这个时候的重载规则呢?是不是这个时候ts不知道httpOptions 的数据类型啊?

抱着试一试的态度(其实也不抱希望,毕竟连js都有类型推导,更何况ts呢),我给httpOptions 加了数据类型:

    const uri = `${this.config.uri}/${this.domain}`;
    const httpOptions: {
      headers?: HttpHeaders | {[header: string]: string | string[];};observe?: 'body';params?: HttpParams | {[param: string]: string | string[];};reportProgress?: boolean;responseType: 'json';withCredentials?: boolean;} = {headers: this.headers,responseType: 'json',params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, httpOptions);

httpOptions 的数据类型就是直接copy Angular的码源定义的,竟然不报Error了。。。。

结合之前报错的信息“{ headers: HttpHeaders; responseType: string; params: HttpParams; }”,就可以发现:

如果没有给出数据类型,ts会根据规则推导出responseType的数据类型是string,但是get方法的15种重载中,responseType的数据类型是"json" | "text" | "blob" | "arraybuffer"。

string类型显然不是"json" | "text" | "blob" | "arraybuffer"其中的一个,所以报了一个error,那是不是不做类型推导就可以了?为了验证这个猜想,我做了如下测试:

    const httpOptions: Object = {headers: this.headers,responseType: 'json',params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, httpOptions);

这样是可以的,考虑一下,数据类型为Object时,就不会再进行类型推导,也就不会报error了。

如果单独把responseType的赋值不提出了呢?

    const uri = `${this.config.uri}/${this.domain}`;const httpOptions = {headers: this.headers,params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, {...httpOptions, responseType: 'json'});

这样同样也是可以的。所以结论就是: 要么给ts正确的推导,要么不要让ts进行推导。

现在再记录一下上面提到的文章:

观点1:

const res = this.http.get(url, {responseType: 'text'});与
const options = {responseType: 'text'}; const res = this.http.get(url, options);是不同的,这是类型推导引起的,对于1:ts不会进行类型推导,对于2:ts把options类型推导为{responseType: string},这就造成了错误。

观点2:

 可以用下面的方法让ts有正确的推导:

    const uri = `${this.config.uri}/${this.domain}`;const httpOptions = {headers: this.headers,responseType: 'json' as 'json',params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, httpOptions);

  这样推导出来的就是{responseType: 'json'},就可以正常显示了。

观点3:

 当然,github上的各位小哥心中都有一个mmp,这个明明是Angular的bug,为什么从4.3版本更新到如今的7.1版本还是没有修改过来?

 为了抗议,有个小哥提出如下方法:

// define this namespace somewhere
export namespace ResponseType {export const JSON = 'json' as 'json';export const ArrayBuffer = 'arraybuffer' as 'arraybuffer';export const Blob = 'blob' as 'blob';export const Text = 'text' as 'text';
}

// import the namespace above and use it like this
const reqOpts = {params: params,headers: headers,responseType: ResponseType.JSON,
};// no type error, the right signature is selected
const a = await this.http.get(url, reqOpts);
const b = await this.http.get<MyClass>(url, reqOpts);

我尝试了一下,是可以用的。如果只是写成这样,那也不足为奇,小哥本着反正是开源软件,就修改一下源代码吧,Angular的开发人员不修改bug,他就自己修改了。

You would need to first put the following in the declaration @angular/common/http/src/client.d.ts:
declare module '@angular/common/http' {export namespace HttpResponseType {export const JSON: 'json';export const ArrayBuffer: 'arraybuffer';export const Blob: 'blob';export const Text: 'text';}export declare type HttpObserve = 'body' | 'events' | 'response';export namespace HttpObserve {export const Body: 'body';export const Events: 'events';export const Response: 'response';}
}

Then implement it in angular/packages/common/http/src/:
export namespace HttpResponseType {export const JSON: 'json' = 'json';export const ArrayBuffer: 'arraybuffer' = 'arraybuffer';export const Blob: 'blob' = 'blob';export const Text: 'text' = 'text';
}export type HttpObserve = 'body' | 'events' | 'response';
export namespace HttpObserve {export const Body: 'body' = 'body';export const Events: 'events' = 'events';export const Response: 'response' = 'response';
}

Finally the two namespaces should be properly exported all the way back to @angular/common/http.

我也尝试了一下,也是可以的,但是还是不建议这样修改啊,毕竟没有经过严格的单元测试,不知道会不会影响全局的代码啊。

总结一下:既然官方没有修改,开发者就要一直打补丁,建议使用如下的方式:

1:

    // 加数据类型const uri = `${this.config.uri}/${this.domain}`;
    const httpOptions: {headers?: HttpHeaders | {[header: string]: string | string[];};observe?: 'body';params?: HttpParams | {[param: string]: string | string[];};reportProgress?: boolean;responseType: 'json';withCredentials?: boolean;} = {headers: this.headers,responseType: 'json',params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, httpOptions);

2:

    // 把特殊的responseType和observe不要提出来const uri = `${this.config.uri}/${this.domain}`;const httpOptions = {headers: this.headers,params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, {...httpOptions, responseType: 'json'});

3:

// 强制正确类型推导    const uri = `${this.config.uri}/${this.domain}`;const httpOptions = {headers: this.headers,responseType: 'json' as 'json',params: (new HttpParams()).set('members_like', userId),};return this.http.get<Project[]>(uri, httpOptions);

4:企业级开发中为了大家使用的方便,建议把类型单独提取出来

 1 // httpOptions的observe,参见https://www.cnblogs.com/wangtingnoblog/p/10322483.html
 2 export namespace ObserveType {
 3   export const Body = 'body' as 'body';
 4   export const Response = 'response' as 'response';
 5   export const Events = 'events' as 'events';
 6 }
 7 // httpOptions的responseType,参见https://www.cnblogs.com/wangtingnoblog/p/10322483.html
 8 export namespace ResponseType {
 9   export const Json = 'json' as 'json';
10   export const Text = 'text' as 'text';
11   export const Blob = 'blob' as 'blob';
12   export const Arraybuffer = 'arraybuffer' as 'arraybuffer';
13 }

使用:

1     const httpOptions = {
2         headers: this.headers,
3         // 如何设置observe和responseType参见https://www.cnblogs.com/wangtingnoblog/p/10322483.html
4         observe: ObserveType.Response,
5         responseType: ResponseType.Json,
6         // 查询参数,会被解析成?members_like=1,可以给HttpParams传递参数HttpParamsOptions(支持数组,查询字符串),
7         // HttpParams是不可修改的,只能通过set返回新的HttpParams
8         params: (new HttpParams()).set('members_like', userId),
9     };

注意第4行 第5行

Over

修改: 2019-02-16 17:34:05

转载于:https://www.cnblogs.com/wangtingnoblog/p/10322483.html

Angular HttpClient responseType和observe的坑人行为相关推荐

  1. Angular4 Angular HttpClient

    安装 首先,我们需要更新所有的包到 4.3.0-rc.0 版本.然后,我们需要在 AppModule 中导入 HttpClientModule 模块.具体如下: import { HttpClient ...

  2. angular HttpClient post put patch del 方法(2)-Promise 服务

    之前做了在一个页面的CRUD的方法,现实中webapi模块往往是单独写服务的,所以修改了一下原来的设计和结构,还是需要很多知识的. 2017.11.15增加patch方法 ,改进服务程序优化写法 20 ...

  3. angular HttpClient 配置

    angular的资料很少,并且坑很多,报错也不是很好解决,研究这个花了好几天时间. 首先,先查看自己的anuglar版本是不是4.3.0以上,HttpClient是anuglar4.3中新加入的特性, ...

  4. Angular HTTPClient的使用方法

    这个例子演示了如何使用Angular的HttpClientModule. 在app.module.ts里导入HttpClientModule: import { HttpClientModule } ...

  5. Angular HttpClient.get的实现单步调试

    进入get方法: request方法实现的源代码: @Injectable() export class HttpClient {constructor(private handler: HttpHa ...

  6. 解决Angular HttpClient设置headers后,Body体格式问题

    Angular headers默认的Content-Type为 multipart/form-data; boundary=----WebKitFormBoundaryTyqm71FGjlNaJxbE ...

  7. angular HttpClient getbyid 方法获取数据

    简单传参的方法,最常用的是getbyid 方法. 直接上代码: getbyId(id: string) {this.myhttp.get('http://192.168.2.139:9002/api/ ...

  8. angular 模块构建_如何通过11个简单的步骤从头开始构建Angular 8应用

    angular 模块构建 Angular is one of the three most popular frameworks for front-end development, alongsid ...

  9. Angular导出功能(excel导出功能、文件数据流导出功能、图片的下载导出功能)

    Angular导出功能(excel导出功能.文件数据流导出功能.图片的下载导出功能) 场景1:(直接返回网络地址进行导出的excel) 后台返回的是 : "http://192.168.0. ...

最新文章

  1. python反转字符串_python翻转字符串
  2. @autowired注解_品Spring:对@Autowired和@Value注解的处理方法(文末附spring系列资源合集)...
  3. 七十二、Springboot整合MyBatis(配置文件版)
  4. 面试必谈的哈希,.Net 程序员温故而知新
  5. 安卓跑linux程序_Android下运行Linux可执行程序
  6. java导入lang_为什么java.lang不需要导放
  7. 【目标检测】FPN(Fature Pyramid Network)详解
  8. Git使用相关问题汇总
  9. linux ssh环境,在Linux下ssh 环境的登录 和 文件拷贝
  10. 去掉超链接的颜色_Word中怎么快速批量删除去掉网站超链接技巧
  11. 震惊!腾讯要建AI鹅厂,百度让狗刷脸购物,锤子R-1真机披露
  12. linux扫描后台地址,Linux如何查看和控制进程
  13. 情感分类——Attention(前篇续)
  14. 【历史上的今天】3 月 7 日:首条海底光缆开通;VeriSign 收购 Network Solutions;计算机图形学先驱诞生
  15. flink-cdc 同步mysql数据到ES时间格式问题。
  16. 学习Python之旅
  17. 中国大学Mooc浙大翁恺老师《零基础学Java语言》编程作业(续)(5~ 7)
  18. 求助 TM1638不能读回按键值
  19. java实现凯撒密码_凯撒密码--java实现
  20. 2022年10月16日 记

热门文章

  1. 鸿蒙os 智慧屏,采用鸿蒙OS的荣耀智慧屏正式亮相 “电视的未来”来了
  2. 微信红包怎么改封面教程
  3. 140个绝对经典的电脑技巧
  4. 智源新星刘利斌:让 AI 无限逼近人类的运动能力
  5. mysql IS NOT NULL优化案例
  6. HG6543C1(2) --- 公网IP端口转发
  7. #!/usr/bin/env python
  8. 微信或许已经不再克制了
  9. PHP实现Java API接口的调用(JavaBridge环境搭建)
  10. 获取linux内核基址,Linux内核漏洞利用技术:覆写modprobe_path