angular - 在 Angular 中,如何创建一个自定义验证器来验证 http 请求?
问题描述
我正在开发一个使用 gitLab 问题来显示图表的应用程序。要使用 api 进行身份验证,我想使用附加到 get 请求的访问令牌,如下所示:
https://gitlab.de/api/v4/issues?private_token=*********** *
我有一个表单,用户可以在其中输入个人访问令牌。我想使用自定义输入验证器验证令牌,并在输入字段下方添加一条错误消息(我使用的是材料角度)。
我使用服务来发出 http 请求:
private makeGetRequest(endpoint: string, params: HttpParams) {
return this.http.get<Issue[]>(this.buildURL(endpoint), {params}).pipe(catchError(this.handleError));
}
public getIssues(): Observable<Issue[]> {
let params = new HttpParams().set('private_token', this.access_token);
return this.makeGetRequest('/issues', params)
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong.
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// Return an observable with a user-facing error message.
return throwError(
'Something bad happened; please try again later.');
}
在组件中,我添加了一个验证器功能。我正在尝试对 api 进行一些调用,然后检查它是否有效。
// Form group with validation
authForm = new FormGroup({
url: new FormControl('gitlab.de'),
token: new FormControl('', [this.validateToken])
});
// Add error message below input fields
getErrorMessage() {
if (this.authForm.controls.token.hasError('required')) {
return 'You must enter a token';
}
return this.authForm.controls.token.hasError('tokenInvalid') ? 'Not a valid token' : '';
}
// Check if token is valid using api
validateToken(control: AbstractControl): { [key: string]: any } | null {
if (control.dirty || control.touched) {
this.apiService.getIssues().subscribe((response) => {}, (error) => {return {'tokenInvalid': true}})
} else {
return null
}
}
有几个教程,但我无法理解它们。当我在输入中输入内容时,我只会在控制台中得到这个输出:错误类型错误:这是未定义的
解决方案
您不需要创建服务,您可以通过 depenceny 注入访问 http 模块。首先设置http模块:
在 app.module.ts 中:
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule],
providers: [],
bootstrap: [AppComponent],
})
创建一个类来编写自定义异步验证器:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { AsyncValidator, FormControl } from '@angular/forms';
import { map, catchError } from 'rxjs/operators';
import { of } from 'rxjs';
// this class needs to use the dependency injection to reach the http client to make an api request
// we can only access to http client with dependecny injection system
// now we need to decorate this class with Injectable
@Injectable({
providedIn: 'root',
})
export class HttpRequestValidation implements AsyncValidator {
// with this code inside the constructor, we access to "http"
constructor(private http: HttpClient) {}
// validate() will be called by the component that implments the
form which has a different context. "this" refers to the context
that calls the function. so in other context, "this" will refer
to that context, so "this.http" will be undefined in that context
cause that context does not have http.
// that is why we use arrow function here. Because, wherever you use
the arrow function, "this" inside arrow will always refer to the
object that it was created in. in this case "this" will always
refer to the current class (HttpRequestValidation). so "this.http"
will work.
validate = (control: FormControl) => {
// if this validator would be used by the FormGroup, you could use "FormGroup" type.
//if you are not sure you can use "control: AbstractControl)"
// looks like, in your case you need for the FormControl
const { value } = control;
return this.http
.post<any>('https://domain/', {
//looks like you are checking for the token
token: value,
})
.pipe(
// errors skip the map(). if we return null, means we got 200 response code
map(() => {
return null;
}),
catchError((err) => {
//check the err obj to see its properties
console.log(err);
if (err.error.token) {
//catchError has to return a new Observable and "of" is a shortcut
return of({ write a meaningful obj});
}
return of({ write a meaningful obj});
})
);
};
}
现在我们你在一个单独的类中编写了这个,是时候将它用于你的表单了。
authForm = new FormGroup({
url: new FormControl('gitlab.plri.de'),
token: new FormControl('', [arrays of sync validators],asyncValidator)
});
FormControl() 有 3 个参数。第一个是初始值,第二个是同步验证器数组,第三个是异步验证器。由于实现异步验证器的成本很高,因此 Angular 将首先解析同步验证器。如果所有同步验证器都验证了输入,则将启动异步验证。
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { HttpRequestValidation } from '../validators/unique-username';
export class SignupComponent implements OnInit {
authForm = new FormGroup({
url: new FormControl('gitlab.plri.de'),
token: new FormControl('', [
Validators.required,
Validators.minLength(3)],
this.httpRequestValidation.validate)
});
constructor(
private httpRequestValidation: HttpRequestValidation
) {}
ngOnInit(): void {}
}
推荐阅读
- python - Django 和 Ajax,获取 Json 序列化程序返回的外键值
- node.js - bash:create-nuxt-app:找不到命令
- java - Spring boot:一个springboot应用可以部署到upcloud或者vultr云服务吗
- reactjs - React Native 我在这里做错了什么
- python - 我如何使用 python 请求登录 instagram
- mysql - MySQL - 在逗号分隔的数字字符串中查找数字
- c - 将 FILE* 指针从 Swift 传递给 C 函数
- pylons - 在 pylons 中,我如何强制用户下载文件?
- javascript - NestJs 异步 httpService 调用
- kannel - 如何在 kannel 几个小时后过期未决交付报告