angular HttpClient

参考:angular httpClient文档

要想使用 HttpClient,就要先导入 Angular 的 HttpClientModule。大多数应用都会在根模块 AppModule 中导入它!!!!。

1.简单的获取 JSON 数据

// assets/config.json
{
  "heroesUrl": "api/heroes",
  "textfile": "assets/textfile.txt"
}

// app/config/config.service.ts
configUrl = 'assets/config.json';
getConfig() {
  return this.http.get(this.configUrl);
}

// app/config/config.component.ts 使用
showConfig() {
  this.configService.getConfig()
    .subscribe((data: Config) => this.config = {
        heroesUrl: data['heroesUrl'],
        textfile:  data['textfile']
    });
}

2.获取错误详情,

请求导致了服务器错误怎么办?
通过在 .subscribe() 中添加第二个回调函数,可以在组件中处理它:
app/config/config.component.ts

showConfig() {
  this.configService.getConfig()
    .subscribe(
      (data: Config) => this.config = { ...data }, // success path
      error => this.error = error // error path
    );
}

封装下:首先要设计一个错误处理器,就像这样

app/config/config.service.ts (handleError),全局错误方法

private handleError(error: HttpErrorResponse) {
  if (error.error instanceof ErrorEvent) {
    console.error('An error occurred:', error.error.message);
  } else {
    console.error(
      `Backend returned code ${error.status}, ` +
      `body was: ${error.error}`);
  }
 
  return throwError('Something bad happened; please try again later.');
};

现在,获取了由 HttpClient 方法返回的 Observable,并把它们通过管道传给错误处理器。
app/config/config.service.ts

getConfig() {
  return this.http.get<Config>(this.configUrl)
    .pipe(
      retry(3), // 重新请求3次
      catchError(this.handleError)
    );
}

3.把数据发送到服务器

除了从服务器获取数据之外(get), HttpClient还支持修改型的请求,通过 PUTPOSTDELETE 这样的 HTTP 方法把数据发送到服务器。

发起一个 POST 请求,(要添加请求头)
app/heroes/heroes.service.ts

import { HttpHeaders } from '@angular/common/http';
// 设置公共请求头
const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type':  'application/json',
    'Authorization': 'my-auth-token'
  })
};

// 添加一条hero数据
addHero (hero: Hero): Observable<Hero> {
  return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
    .pipe(
      catchError(this.handleError('addHero', hero))
    );
}

// 在组组件中使用:this.heroesService.addHero(newHero).subscribe(hero => this.heroes.push(hero));

发起 DELETE 请求
app/heroes/heroes.service.ts添加代码:

/** 删除id为..的hero */
deleteHero (id: number): Observable<{}> {
  const url = `${this.heroesUrl}/${id}`; // DELETE api/heroes/42
  return this.http.delete(url, httpOptions)
    .pipe(
      catchError(this.handleError('deleteHero'))
    );
}

// 组件中使用:this.heroesService.deleteHero(hero.id).subscribe();
// 切记:必须调用 subscribe()订阅。仅仅调用 HeroesService.deleteHero() 是不会发起 DELETE 请求的

发起 PUT 请求
app/heroes/heroes.service.ts添加代码:

/** 更新服务器上的英雄。成功后返回更新的英雄. */
updateHero (hero: Hero): Observable<Hero> {
  return this.http.put<Hero>(this.heroesUrl, hero, httpOptions)
    .pipe(
      catchError(this.handleError('updateHero', hero))
    );
}

高级用法

 1.set()方法修改请求头
直接修改前述配置对象中的现有头,因为这个 HttpHeaders 类的实例是不可变的,
改用 set() 方法代替。 它会返回当前实例的一份克隆,其中应用了这些新修改。
比如在发起下一个请求之前,如果旧的令牌已经过期了,你可能还要修改认证头。

httpOptions.headers = httpOptions.headers.set('Authorization', 'my-new-auth-token');

2.set()方法设置URL 参数
添加 URL 搜索参数也与此类似。 这里的 searchHeroes 方法会查询名字中包含搜索词的英雄列表。

/* 获得名字包含搜索词的英雄*/
searchHeroes(term: string): Observable<Hero[]> {
  term = term.trim();

  // 如果有搜索项,则添加安全的、url编码的搜索参数,HttpParams 是不可变的,所以要使用 set() 方法来修改这些选项
  const options = term ? { params: new HttpParams().set('name', term) } : {};

  return this.http.get<Hero[]>(this.heroesUrl, options)
    .pipe(
      catchError(this.handleError<Hero[]>('searchHeroes', []))
    );
}

3.请求的防抖(debounce) 请移步官网文档的小案例angular 请求防抖

4.switchMap()
这个 switchMap() 操作符有三个重要的特征:
    ①.它的参数是一个返回 Observable 的函数。PackageSearchService.search 会返回 Observable,其它数据服务也一样。
    ②.如果以前的搜索结果仍然是在途状态(这会出现在慢速网络中),它会取消那个请求,并发起一次新的搜索
    ③.它会按照原始的请求顺序返回这些服务的响应,而不用关心服务器实际上是以乱序返回的它们。

5.拦截请求和响应
HTTP 拦截机制是 @angular/common/http 中的主要特性之一。 使用这种拦截机制,你可以声明一些拦截器,用它们监视和转换从应用发送到服务器的 HTTP 请求。 拦截器还可以用监视和转换从服务器返回到本应用的那些响应。 多个选择器会构成一个“请求/响应处理器”的双向链表。
如果没有拦截机制,那么开发人员将不得不对每次 HttpClient 调用显式实现这些任务

编写拦截器
要实现拦截器,就要实现一个实现了 HttpInterceptor 接口中的 intercept() 方法的类。这里是一个什么也不做的空白拦截器,它只会不做任何修改的传递这个请求。
app/http-interceptors/noop-interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';

import { Observable } from 'rxjs';

/** 将未触及的请求传递给下一个请求处理程序。 */
@Injectable()
export class NoopInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    return next.handle(req);
  }
}

app/http-interceptors/index.ts

/* http拦截器的“桶” */
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NoopInterceptor } from './noop-interceptor';

/** 外部的http拦截器提供程序 */
export const httpInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
 // { provide: HTTP_INTERCEPTORS, useClass: EnsureHttpsInterceptor, multi: true },
 // { provide: HTTP_INTERCEPTORS, useClass: TrimNameInterceptor, multi: true },
];

app/app.module.ts

providers: [
  httpInterceptorProviders
],

拦截器的顺序
Angular 会按照你提供它们的顺序应用这些拦截器。 如果你提供拦截器的顺序是先 A,再 B,再 C,那么请求阶段的执行顺序就是 A->B->C,而响应阶段的执行顺序则是 C->B->A。
以后你就再也不能修改这些顺序或移除某些拦截器了。 如果你需要动态启用或禁用某个拦截器,那就要在那个拦截器中自行实现这个功能

不可变性
虽然拦截器有能力改变请求和响应,但 HttpRequest 和 HttpResponse 实例的属性却是只读(readonly)的, 因此让它们基本上是不可变的
通过把 HttpRequest 的属性设置为只读的,TypeScript 可以防止你犯这种错误。

req.url = req.url.replace('http://', 'https://');

要想修改该请求,就要先克隆它,并修改这个克隆体,然后再把这个克隆体传给 next.handle()。 你可以用一步操作中完成对请求的克隆和修改,例子如下:
app/http-interceptors/ensure-https-interceptor.ts

// 同时克隆请求并将“http://”替换为“https://”
const secureReq = req.clone({
  url: req.url.replace('http://', 'https://')
});
// 将克隆的“安全”请求发送到下一个处理程序。
return next.handle(secureReq);

这个 clone() 方法的哈希型参数允许你在复制出克隆体的同时改变该请求的某些特定属性。
如果你必须修改请求体,那么就要先复制它,然后修改这个副本,clone() 这个请求,然后把这个请求体的副本作为新的请求体,例子如下:
app/http-interceptors/trim-name-interceptor.ts 

// 复制正文并从name属性中修剪空白
const newBody = { ...body, name: body.name.trim() };
// 克隆请求并设置其主体
const newReq = req.clone({ body: newBody });
// 将克隆的请求发送到下一个处理程序。
return next.handle(newReq);

清空请求体

newReq = req.clone({ ... }); // 未提及正文=>保留原始正文
newReq = req.clone({ body: undefined }); // 保留原始实体
newReq = req.clone({ body: null }); // 清空请求体

设置默认请求头
app/http-interceptors/auth-interceptor.ts

import { AuthService } from '../auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private auth: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    // 从服务获取身份验证令牌
    const authToken = this.auth.getAuthorizationToken();

    // 克隆请求并将原始头替换为
    // 克隆的头,使用授权更新。
    const authReq = req.clone({
      headers: req.headers.set('Authorization', authToken)
    });
    // const authReq = req.clone({ setHeaders: { Authorization: authToken } });

    // 将带有头的克隆请求发送到下一个处理程序。
    return next.handle(authReq);
  }
}

拦截器还可以做些什么?
1.记日志  参考:angular官网-拦截器记日志
2.缓存   参考:angular官网-拦截器缓存

安全:XSRF 防护  参考:angular 安全防护

配置自定义 cookie/header 名称
如果你的后端服务中对 XSRF 令牌的 cookie 或 头使用了不一样的名字,就要使用 HttpClientXsrfModule.withConfig() 来覆盖掉默认值。

imports: [
  HttpClientModule,
  HttpClientXsrfModule.withOptions({
    cookieName: 'My-Xsrf-Cookie',
    headerName: 'My-Xsrf-Header',
  }),
],

总结: 

post请求(新增数据):this.http.post<Hero>(url, params, httpHeaderOption) // httpHeaderOption设置的请求头
delete请求(删除数据):this.http.delete(url, httpHeaderOption)
put请求(修改数据):this.http.put<Hero>(url, params, httpHeaderOption)
get请求(获取数据):this.http.get(url);

拦截器:intercept(req, next){...  return next(setreq)},  // setreq是对req进行修改过后的请求。

set()方法设置URL 参数set()方法修改请求头
httpOptions.headers = httpOptions.headers.set('Authorization', 'my-new-auth-token');//还可以使用setHeaders({key:value})
params = { params: new HttpParams().set('name', term) }

get(): resp.headers.get(key)

HttpRequest 和 HttpResponse 实例的属性都是只读的,不可修改,
例如:req.url = req.url.replace('http://', 'https://');,是错误的,只能clone一份出来,再进行修改

清空请求体:req.clone({ body: null })

发布了112 篇原创文章 · 获赞 149 · 访问量 55万+

猜你喜欢

转载自blog.csdn.net/l284969634/article/details/102798373