首页 > 解决方案 > 在 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
    }
  }

有几个教程,但我无法理解它们。当我在输入中输入内容时,我只会在控制台中得到这个输出:错误类型错误:这是未定义的

标签: angularangular-materialangular-httpclientangular-validation

解决方案


您不需要创建服务,您可以通过 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 {}
}

推荐阅读