Angular4 - http

1. http

(1) 简单示例

http使用之前需要在当前导入HttpModel。然后看一个例子:
//httml:
<h3>Angular Orgs Members</h3>
<ul *ngIf="members">
  <li *ngFor="let member of members;">
    <p>
      <img [src]="member.avatar_url" width="48" height="48"/>
      ID:<span>{{member.id}}</span>
      Name: <span>{{member.login}}</span>
    </p>
  </li>
</ul>
//JS:
import {Component, OnInit} from '@angular/core';
import { Http } from '@angular/http'; // (1)
import 'rxjs/add/operator/map'; // (2)

interface Member {
  id: string;
  login: string;
  avatar_url: string;
}
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  members: Member[];

  constructor(private http: Http) { } // (3)

  ngOnInit() {
    this.http.get(`https://api.github.com/orgs/angular/members?page=1&per_page=5`) // (4)
      .map(res => res.json()) // (5)
      .subscribe(data => {
        if (data) this.members = data; // (6)
      });
  }
}
上面标注序号的step的步骤说明:
  (1) 从 @angular/http 模块中导入 Http 类
  (2) 导入 RxJS 中的 map 操作符
  (3) 使用 DI 方式注入 http 服务的
  (4) 调用 http 服务的 get() 方法,设置请求地址并发送 HTTP 请求
  (5) 调用 Response 对象的 json() 方法,把响应体转成 JSON 对象
  (6) 把请求的结果,赋值给 members 属性

(2) Class file

a. Http Class

class Http {
  constructor(_backend: ConnectionBackend, _defaultOptions: RequestOptions)

  request(url: string | Request, options?: RequestOptionsArgs): Observable<Response>

  get(url: string, options?: RequestOptionsArgs): Observable<Response>

  post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response>

  put(url: string, body: any, options?: RequestOptionsArgs): Observable<Response>

  delete(url: string, options?: RequestOptionsArgs): Observable<Response>

  //PATCH方法是新引入的,是对PUT方法的补充,用来对已知资源进行**局部更新** (PUT 是幂等的,而 PATCH 不是幂等的。)
  patch(url: string, body: any, options?: RequestOptionsArgs): Observable<Response>

  //只请求页面的首部
  head(url: string, options?: RequestOptionsArgs): Observable<Response>

  //1、获取服务器支持的HTTP请求方法;
  //2、用来检查服务器的性能。
  options(url: string, options?: RequestOptionsArgs): Observable<Response>
}
可以看到上面内置的请求方法的返回值是一个可观测的ResPonse对象(所以必须有观察者请求才会发出,也就是(1)中示例中的subscribe必须要存在),每个方法的参数均有RequestOptionsArgs。接下来我们看一下这两个类含有什么?

b. RequestOptions class

class RequestOptions {
  constructor(opts: RequestOptionsArgs = {})
  //请求方式
  method: RequestMethod | string | null
  //请求头
  headers: Headers | null
  //请求体
  body: any
  //请求的地址
  url: string | null

  //url 请求参数 (params与search都会在url的后面添加参数)
  params: URLSearchParams
  search: URLSearchParams

  //和跨域相关,表明在进行跨域访问控制请求,是否使用认证信息(cookie或者header)
  withCredentials: boolean | null
  //返回类型
  responseType: ResponseContentType | null
  merge(options?: RequestOptionsArgs): RequestOptions
}
举个例子:
let headersContent = new Headers();
headersContent.set('name', 'rodchen');

let paramsContent = new URLSearchParams();
paramsContent.set('parmsName', 'rodchen');

let searchContent = new URLSearchParams();
paramsContent.set('searName', 'rodchen-search');

let requestOptions = new RequestOptions({
  url: 'portal/applications',
  method: 'Get',
  headers: headersContent,
  body: {},
  params: paramsContent,
  search: searchContent,
  withCredentials: true,
  responseType: ResponseContentType.Json
});

this.http.request('portal/applications', requestOptions)
  .subscribe(data => {
        console.log(data);
  });
然后看一下页面的发送请求:


可以看到我们设置的header,以及查询参数都已经在request中显示出来。

c. Request class

class Request extends Body {
  constructor(requestOptions: RequestArgs)
  method: RequestMethod
  headers: Headers
  url: string

  //表明在进行跨域访问控制请求,是否使用认证信息(cookie或者header)
  withCredentials: boolean

  //预期返回值的类型
  responseType: ResponseContentType

  //返回基于请求头的ContentType值
  detectContentType(): ContentType
  //返回基于请求体的ContentType值
  detectContentTypeFromBody(): ContentType
  //基于contentType返回请求体
  getBody(): any
}
其实可以看出内部成员和RequestOptions类似,那我们就说一下content-type.

MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。

Angular中的ContentType类型如下:
export declare enum ContentType {
    NONE = 0,
    JSON = 1,
    FORM = 2,
    FORM_DATA = 3,
    TEXT = 4,
    BLOB = 5,
    ARRAY_BUFFER = 6,
}
这里的类型是由我们发送数据的时候决定,例如我们上面的例子是get方式,这里的结果就是本应该是0,但是由于上面添加了body属性,所以这里得到的结果是1,其他的就没有去测试了。

d. Response class

class Response extends Body {
  constructor(responseOptions: ResponseOptions)
  // "basic", "cors", "default", "error", or "opaque",默认"default"
  type: ResponseType
  //当status在200-299范围内,该值为true
  ok: boolean
  //响应的URL地址,默认为空字符串
  url: string
  //服务器返回的状态,默认为200
  status: number
  //请求的响应状态信息,默认值是"OK"
  statusText: string | null
  //非标准属性:用于表示已加载响应体的字节数
  bytesLoaded: number
  //非标准属性:表示响应体总字节数
  totalBytes: number
  //响应头对象
  headers: Headers | null

  toString(): string {
    return `Response with status: ${this.status} ${this.statusText} for URL: ${this.url}`;
  }
}
然后看一下上面(3)中的返回response


这里的type返回为2:
export declare enum ResponseType {
    Basic = 0,
    Cors = 1,
    Default = 2,
    Error = 3,
    Opaque = 4,
}

f. Body class

可以看出Resquest和Resposne都是继承与Body。
export abstract class Body {
  protected _body: any;

  json(): any { // 转化为JSON对象 - 具体应用:map(res => res.json())
    if (typeof this._body === 'string') {
      return JSON.parse(<string>this._body);
    }
    if (this._body instanceof ArrayBuffer) {
      return JSON.parse(this.text());
    }
    return this._body;
  }

  // 转换为Text文本
  text(): string { ... }

  // 转换为ArrayBuffer对象
  arrayBuffer(): ArrayBuffer { ... }

  // 转换为Blob对象
  blob(): Blob { ... }
}
看到这里会发现一个我们经常使用的方法json().

(3) http 错误处理

我们还是将上面的请求先拿下来(具体的requestOptions部分就不copy下来了)
this.http.request('portal/applications', requestOptions)
  .subscribe(data => {
        console.log(data);
  });
}
还是先说一下http集成了Rxjs,现在this.http.request('portal/applications', requestOptions)是一个Obserable对象。由于Rx.Observable.catch(...args)用来处理异常,所以我们这里也可以使用.catch()处理异常。如下

import 'rxjs/add/operator/catch';

this.http.request('portal/applications', requestOptions)
.catch(error => {
	console.error("error catched", error);
	//return Observable.throw(error);
	return Observable.of({description: "Error Value Emitted"})
})
.subscribe(
	data => {
	    console.log(data);
	},
	error2 => {
	  console.log(error2);
	}
);
这里说明两点一个是import 'rxjs/add/operator/catch',如果没有引入,catch会使用其他包里的东西。 第二个是catch的返回值必须是Observable对象,这里是将error继续抛给subscribe中的错误处理函数。

如果我在catch中没有抛出exception,那么error2处理函数就不有任何的内容输出,例如:
//注意这里的引入
import 'rxjs/add/observable/of'; 

this.http.request('portal/applications', requestOptions)
  .catch(error => {
    console.error("error catched", error);
    //return Observable.throw(error);
    return Observable.of({description: "Error Value Emitted"})
  })
  .subscribe(
    data => {
        console.log(data);
    },
    error2 => {
      console.log(error2);
  });

然后我们再看一下结果:

现在已经没有error2错误处理函数输出的结果了。

那么这个处理的意义在于什么呢,应该是对所有的请求进行一个全局错误处理,这样我们可以在这个错误处理函数进行一些

this.http.request('portal/applications', requestOptions)
  .catch(this.handleError)
  .subscribe(
    data => {
        console.log(data);
    },
    error2 => {
      console.log(error2);
  });

private handleError(error: any) {
	let errorMessage = '';

	if (error.message === 'Timeout has occurred') {
	  errorMessage = 'request.timeout';
	} else if (error.status === 400) {
	  errorMessage = 'tokenError, you will navigate home page after 2s!';
	  setTimeout(function(){
	    window.location.href = environment.guidancePageUrl;
	  }, 2000 );
	} else if (error.status === 500) {
	  errorMessage = 'service.not.connect';
	} else if (error.status === 401) {
	  errorMessage = JSON.parse(error._body).message;
	} else if (error.status === 403) {
	  errorMessage =  '403 : ' + error.json().message;
	}

	return Observable.throw(errorMessage);
}
这样我们就可以全面管理页面上错误信息的弹出(后台没有考虑页面上错误信息的弹出情况下)。

(4) Rxjs相关的处理

首先需要说明的一定要对应的内容引入进来,例如
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';

a. mergeMap

mergeMap 操作符用于从内部的 Observable 对象中获取值,然后返回给父级流对象。

合并 Observable 对象 ( jsBin)

当我们发送下一个请求时,需要依赖于上一个请求的数据。即我们在需要在上一个请求的回调函数中获取相应数据,然后在发起另一个 HTTP 请求。

import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';

@Component({
  selector: 'app-root',
  template: `
    <p>{{username}} Detail Info</p>
    {{user | json}}
  `
})
export class AppComponent implements OnInit {
  constructor(private http: Http) { }

  apiUrl = 'https://jsonplaceholder.typicode.com/users';
  username: string = '';
  user: any;

  ngOnInit() {
    this.http.get(this.apiUrl)
      .map(res => res.json())
      .mergeMap(users => {
        this.username = users[6].username;
        return this.http.get(`${this.apiUrl}?username=${this.username}`)
          .map(res => res.json())
      })
      .subscribe(user => this.user = user);
  }
}


b.switchMap 

也可以达到上面的情况
sequentialRequests() {
    const sequence$ = this.http.get<Course>('/courses/-KgVwEBq5wbFnjj7O8Fp.json')
        .switchMap(course => {
            course.description+= ' - TEST ';
            return this.http.put('/courses/-KgVwEBq5wbFnjj7O8Fp.json', course)
        });
        
    sequence$.subscribe();
}
switchMap 操作符用于对源 Observable 对象发出的值,做映射处理。若有新的 Observable 对象出现,会在新的 Observable 对象发出新值后,退订前一个未处理完的 Observable 对象。比如实现 AutoComplete 功能,我们可以利用 switchMap 操作符,来取消无用的 Http 请求。

c. forkJoin

forkJoin 是 Rx 版本的 Promise.all(),即表示等到所有的 Observable 都完成后,才一次性返回值。
Observable.forkJoin(this.http.request('portal/applications', requestOptions), this.http.request('portal/applications', requestOptions))
.subscribe(
data => {
    console.log(data);
});
这里data输出的是两个请求结果的数组

2. HttpClient

http使用之前需要在当前导入HttpClientModel. 然后在构造函数中引入
constructor(private http: HttpClient) { }

(1) HttpClient请求

class HttpClient {
  constructor(handler: HttpHandler)
  request(first: string | HttpRequest<any>, url?: string, options: {...}): Observable<any>
  delete(url: string, options: {...}): Observable<any>
  get(url: string, options: {...}): Observable<any>
  head(url: string, options: {...}): Observable<any>
  jsonp<T>(url: string, callbackParam: string): Observable<T>
  options(url: string, options: {...}): Observable<any>
  patch(url: string, body: any | null, options: {...}): Observable<any>
  post(url: string, body: any | null, options: {...}): Observable<any>
  put(url: string, body: any | null, options: {...}): Observable<any>
}
//rquest请求
request(method: string, url: string, options: {
    body?: any;
    headers?: HttpHeaders | {
        [header: string]: string | string[];
    };
    observe?: 'body';
    params?: HttpParams | {
        [param: string]: string | string[];
    };
    reportProgress?: boolean;
    responseType: 'arraybuffer';
    withCredentials?: boolean;
}): Observable<ArrayBuffer>
然后举个例子,将上面的例子改写一下:
let headersContent = new HttpHeaders();
headersContent.set('name', 'rodchen');

let paramsContent = new HttpParams();
paramsContent.set('parmsName', 'rodchen');

let searchContent = new URLSearchParams();
paramsContent.set('searName', 'rodchen-search');

this.http.request('GET', 'portal/applications', {
  headers: headersContent,
  params: paramsContent,
  responseType: 'json'
})
  .catch(error => {
    console.error("error catched", error);
    return Observable.throw(error);
  })
  .subscribe(
    data => {
        console.log(data);
    },
    error2 => {
      console.log(error2);
  });
其实具体的用法直接查找 官方文档就好。

至于其他的请求和request类似,只是参数缺少了一个method,例如get
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>

(2) 新的特性

a.  默认JSON解析

在http的时候我们需要使用json函数
http.get(url).map(res => res.json()).subscribe(...)
httpClient则自己做了这个事情:
httpClient.get(url).subscribe(...)

b. 拦截器

由于没有使用这个,详情可以查看文章

c. 进度事件

进度事件可以用于跟踪文件上传和下载。详情可以查看文章


参考:

https://www.angular.cn/guide/http

https://segmentfault.com/a/1190000010116848

https://segmentfault.com/a/1190000010259536

猜你喜欢

转载自blog.csdn.net/it_rod/article/details/79811009