全文共5358字,预计学习时长16分钟
本文将讨论如何在Angular应用程序中执行请求。
1. 使用拦截器来装饰请求
2. HttpClient 与HttpBackend的对比
3. 其他方式
这篇文章建立在我多年进行前端工作的经验(4年Angular工作经验)上。如有错误之处,请不吝赐教。
使用拦截器来装饰请求
使用API(应用程序接口)工作时, HTTP拦截器是一项主要特征。通过拦截,您使用拦截器,以检查HTTP请求并将其从应用程序发送到服务器,并从服务器返回应用程序的响应中进行转换。
简单来说:
在实际操作中,用户登录后,每个向后端传输的API都需要在标头添加授权,以核实用户的身份及授权。
例1:使用拦截器向请求添加JWT
@Injectable()
exportclass RequestInterceptorService implements HttpInterceptor{
constructor(private store: Store) { }
intercept(
request: HttpRequest, next: HttpHandler
): Observable {
const userInfo = this.store.selectSnapshot(state => state.AppState.user);
const authReq = request.clone({
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${userInfo.token}`
})
});
return next.handle(authReq);
}
}
使用拦截器向请求添加jwt
例2: 使用拦截器控制应用程序上的微调器
@Injectable({
providedIn: 'root'
})
exportclass LoadingInterceptorService {
activeRequests: number =0;
constructor(
private loadingScreenService: LoadingService
) { }
intercept(request: HttpRequest, next: HttpHandler): Observable> {
if (this.activeRequests===0) {
this.loadingScreenService.startLoading();
}
this.activeRequests++;
return next.handle(request).pipe(
finalize(() => {
this.activeRequests--;
if (this.activeRequests===0) {
this.loadingScreenService.stopLoading();
}
})
)
};
}
使用拦截器控制应用程序上的微调器
例 3: 使用拦截器来高速缓存请求
@Injectable()
exportclass CachingInterceptor implements HttpInterceptor {
constructor(private cache: RequestCache) {}
intercept(req: HttpRequest, next: HttpHandler) {
// continue if not cachable.
if (!isCachable(req)) { return next.handle(req); }
const cachedResponse = this.cache.get(req);
return cachedResponse ?
of(cachedResponse) : sendRequest(req, next, this.cache);
}
}
使用拦截器来高速缓存请求
也可以做更多与请求有关的事情:
· 在API失败的情况下显示模型。
· 响应后根据状态代码添加更多操作规则
· 覆盖URL请求或请求正文
拦截器将影响您的所有请求。那么,如何在Angular中自定义请求并禁用拦截器呢?下面来一起看看
HttpClient与HttpBackend的对比
我观察到很多开发人员仅在他们需要向后端发送请求时,才在Angular项目中使用HttpClient。而这是不正确的。
为什么?
因为我们由HttpClient发送的请求总是经过Angular的拦截器。为了解决这个问题,一些开发人员编写更多代码以禁用拦截器,我称之为“作弊代码”。而一些开发人员并不会使用拦截器来处理请求,并向每个请求添加标头。
这使代码充满代码异味和技术债务。开发人员必须使用上述两个选项之一来完成更多的工作。
解决办法: 使用 HttpClient 和HttpBackend 来发送请求。
唯一不同之处在于 HttpBackend 将请求直接发送到后端,而不经过拦截器链。
HttpService样本:
import { Injectable } from'@angular/core';
import { HttpHeaders, HttpClient, HttpParams, HttpBackend } from'@angular/common/http';
import { Observable, throwError, of } from'rxjs';
import { catchError } from'rxjs/operators';
@Injectable({
providedIn: 'root'
})
exportclass HttpService {
privatehttpWithoutInterceptor: HttpClient;
constructor(
private http: HttpClient,
private httpBackend: HttpBackend
) {
this.httpWithoutInterceptor=new HttpClient(httpBackend);
}
privateformatErrors(error: any) {
returnthrowError(error.error);
}
get(path: string, params: HttpParams =new HttpParams()): Observable {
returnthis.http.get(`${path}`, { params })
.pipe(catchError(this.formatErrors));
}
put(path: string, body: Object = {}): Observable {
returnthis.http.put(
`${path}`,
JSON.stringify(body)
).pipe(catchError(this.formatErrors));
}
post(path: string, body: Object = {}, options: Object = {}): Observable {
returnthis.http.post(
`${path}`,
JSON.stringify(body),
options
).pipe(catchError(this.formatErrors));
}
delete(path): Observable {
returnthis.http.delete(
`${path}`
).pipe(catchError(this.formatErrors));
}
_get(path: string, params: HttpParams =new HttpParams()): Observable {
returnthis.httpWithoutInterceptor.get(`${path}`, { params })
.pipe(catchError(this.formatErrors));
}
_put(path: string, body: Object = {}): Observable {
returnthis.httpWithoutInterceptor.put(
`${path}`,
JSON.stringify(body)
).pipe(catchError(this.formatErrors));
}
_post(path: string, body: Object = {}, options: Object = {}): Observable {
returnthis.httpWithoutInterceptor.post(
`${path}`,
JSON.stringify(body),
options
).pipe(catchError(this.formatErrors));
}
_delete(path): Observable {
returnthis.httpWithoutInterceptor.delete(
`${path}`
).pipe(catchError(this.formatErrors));
}
}
view rawhttp.service.ts hosted with by GitHub
使用httpclient和httpbackend的httpservice样本
您将做出决定,哪种方式经过拦截器,哪种方式不经过。
对于一些特定的API(应用程序接口)需要不同的标头或添加其他信息,才可以使用 HttpBackend来手动对其进行自定义。
其他方式
我以此作为标题是因为下面是一些您可能遇到过或没有遇到过的小用例。
失败后如何调用API?
getConfig() {
returnthis.http.get(this.configUrl)
.pipe(
retry(3), // retry a failed request up to 3 times
);
}
重试失败的请求,最多三次
如果请求失败,使用retry()(异常重试)来延长尝试该请求的时间。
如何捕捉错误?
建议使用next()函数捕捉拦截器的错误。
@Injectable()
exportclass ErrorInterceptorService implements HttpInterceptor {
constructor() { }
intercept(
request: HttpRequest, next: HttpHandler
): Observable {
return next.handle(request).pipe(
catchError(this.handleError)
);
}
privatehandleError(error: HttpErrorResponse) {
if (error.errorinstanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(
`Backend returned code ${error.status}, `+
`body was: ${error.error}`);
}
returnthrowError(
'Something bad happened; please try again later.');
};
}
如何避免 XSRF攻击?
执行HTTP 请求时, 拦截器从cookie读取令牌,默认为XSRF-TOKEN,并将其设置为 HTTP 标头, X-XSRF-TOKEN。由于只有在您的域上运行的代码才能读取cookie,因此后端可以确定HTTP请求来自您的客户端应用程序而不是攻击者。
如果后端服务对XSRF令牌cookie或标头使用不同的名称,则使用HttpClientXsrfModule.withOptions() 来覆盖。
imports: [
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'My-Xsrf-Cookie',
headerName: 'My-Xsrf-Header',
}),
],
使用HttpClientXsrfModule 覆盖 XSRF令牌
本文包含了所有使用Angular应用程序的API时的必要信息,包括:
· 使用拦截器来管理请求和响应
· 如何使用HttpClient和HttpBackend
· 请求失败后,如何重新调回,如何捕捉错误和避免XSRF攻击。
希望此文章对您有帮助!
留言点赞关注
我们一起分享AI学习与发展的干货
如转载,请后台留言,遵守转载规范