//httml:
Angular Orgs Members
-
ID:{{member.id}}
Name: {{member.login}}
//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的步骤说明:
class Http {
constructor(_backend: ConnectionBackend, _defaultOptions: RequestOptions)
request(url: string | Request, options?: RequestOptionsArgs): Observable
get(url: string, options?: RequestOptionsArgs): Observable
post(url: string, body: any, options?: RequestOptionsArgs): Observable
put(url: string, body: any, options?: RequestOptionsArgs): Observable
delete(url: string, options?: RequestOptionsArgs): Observable
//PATCH方法是新引入的,是对PUT方法的补充,用来对已知资源进行**局部更新** (PUT 是幂等的,而 PATCH 不是幂等的。)
patch(url: string, body: any, options?: RequestOptionsArgs): Observable
//只请求页面的首部
head(url: string, options?: RequestOptionsArgs): Observable
//1、获取服务器支持的HTTP请求方法;
//2、用来检查服务器的性能。
options(url: string, options?: RequestOptionsArgs): Observable
}
可以看到上面内置的请求方法的返回值是一个可观测的ResPonse对象(所以必须有观察者请求才会发出,也就是(1)中示例中的subscribe必须要存在),每个方法的参数均有RequestOptionsArgs。接下来我们看一下这两个类含有什么?
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);
});
然后看一下页面的发送请求:
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,其他的就没有去测试了。
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
export declare enum ResponseType {
Basic = 0,
Cors = 1,
Default = 2,
Error = 3,
Opaque = 4,
}
export abstract class Body {
protected _body: any;
json(): any { // 转化为JSON对象 - 具体应用:map(res => res.json())
if (typeof this._body === 'string') {
return JSON.parse(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().
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中的错误处理函数。//注意这里的引入
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);
}
这样我们就可以全面管理页面上错误信息的弹出(后台没有考虑页面上错误信息的弹出情况下)。
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';
合并 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: `
{{username}} Detail Info
{{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);
}
}
sequentialRequests() {
const sequence$ = this.http.get('/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 请求。
Observable.forkJoin(this.http.request('portal/applications', requestOptions), this.http.request('portal/applications', requestOptions))
.subscribe(
data => {
console.log(data);
});
这里data输出的是两个请求结果的数组
constructor(private http: HttpClient) { }
class HttpClient {
constructor(handler: HttpHandler)
request(first: string | HttpRequest, url?: string, options: {...}): Observable
delete(url: string, options: {...}): Observable
get(url: string, options: {...}): Observable
head(url: string, options: {...}): Observable
jsonp(url: string, callbackParam: string): Observable
options(url: string, options: {...}): Observable
patch(url: string, body: any | null, options: {...}): Observable
post(url: string, body: any | null, options: {...}): Observable
put(url: string, body: any | null, options: {...}): Observable
}
//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
然后举个例子,将上面的例子改写一下:
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);
});
其实具体的用法直接查找 官方文档就好。
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
http.get(url).map(res => res.json()).subscribe(...)
httpClient则自己做了这个事情:
httpClient.get(url).subscribe(...)
进度事件可以用于跟踪文件上传和下载。详情可以查看文章
参考:
https://www.angular.cn/guide/http
https://segmentfault.com/a/1190000010116848
https://segmentfault.com/a/1190000010259536