首页 > 解决方案 > Angular HttpClient 在 Router.navigate() 之后停止工作。收到 400(错误请求)

问题描述

由于某种原因,在我使用路由器导航后,HttpClient 请求开始接收 400(错误请求)。在导航之前,HttpClient 使用完全相同的 http 调用方法可以正常工作。这是导航:

let navigationExtras: NavigationExtras = {
  queryParams: {
    "certificate": JSON.stringify(this.selection.selected[0])
  }
};
this.router.navigate(["create"],  navigationExtras);

这是错误: 在此处输入图像描述

我知道这不是 ASP.NET Core 控制器中的 CORS 问题,因为在导航之前不会发生错误请求,并且我确实允许在服务器端使用 CORS。

HistoryComponent 和 CreateCertComponent 的代码:

  @Component({
  selector: 'app-history',
  templateUrl: './history.component.html',
  styleUrls: ['./history.component.css']
})
export class HistoryComponent implements OnInit {

  certificates: CertificateModel[] = [];
  dataSource : MatTableDataSource<CertificateModel> = new MatTableDataSource(this.certificates);
  displayedColumns: string[] = ['name', 
                                'customer', 
                                'customerOrderNo', 
                                'certificateNo',
                                'orderACKNNo',
                                'date',
                                'select'];
  @ViewChild(MatSort) sort: MatSort
  @ViewChild("paginator") paginator: MatPaginator;
  searchTextfield: string;
  searchOptions: string[] = ["Name", "Customer", "Cust. Ord. No.", "Order ACKN No.", "Date"];
  selectedSearchOption: string = "Name";
  selection = new SelectionModel<CertificateModel>(true, []);
  isLoading:  boolean;

  constructor(private certHttpService: CertificateHttpService,
    public errorDialog: MatDialog,
    private router: Router
    ) { }

  ngOnInit() {
    this.configureDataSource();
    this.loadCertificates();
  }

  loadCertificates() {
    this.isLoading = true;
    this.certHttpService.getAllPro().then((res => {
      this.certificates = res; 
      this.dataSource.data = this.certificates;
      this.isLoading = false;
    }).bind(this))
    .catch((error => {
      this.isLoading = false;
      this.openErrorDialog("Error", error.name + ": " + error.message);
    }).bind(this));
  }

  openErrorDialog(title: string, text: string) {
    var data = {
      "title": title,
      "text": text
    };

    const dialogRef = this.errorDialog.open(ErrorDialogComponent, {
      width: '30vw',
      height: 'auto',
      disableClose: true,
      data: data
    });

    dialogRef.afterClosed().subscribe(result => {
      console.log('The dialog was closed');
    });
  }

  editClicked() {
    let navigationExtras: NavigationExtras = {
      queryParams: {
        "certificate": JSON.stringify(this.selection.selected[0])
      }
    };
    this.router.navigate(["create"],  navigationExtras);
  }

  deleteClicked() {
    this.certHttpService.deletePro(this.selection.selected).then((res => {
      this.selection.selected.forEach(e0 => {
        var index = this.certificates.findIndex(e1 => {
          return e0.guid == e1.guid;
        });
        this.certificates.splice(index, 1);
      });
      this.dataSource = new MatTableDataSource<CertificateModel>(this.certificates);
      this.configureDataSource();
      this.selection = new SelectionModel<CertificateModel>(true, []);
    }).bind(this));
  }

  searchFieldOnChange() {
    console.log("searchfield on change!");
    
  }
  
  applyFilter(filterValue: string) {
    filterValue = filterValue.toLowerCase();
    this.dataSource.filter = filterValue;
  }

  isAllSelected() : boolean {
    const numSelected = this.selection.selected.length;
    const numRows = this.dataSource.data.length;
    return numSelected == numRows;
  }

  masterToggle() {
    this.isAllSelected() ?
      this.selection.clear() :
      this.dataSource.data.forEach(row => this.selection.select(row));
  }

  configureDataSource () {
    this.dataSource.sortingDataAccessor = (data: any, sortHeaderId: string): string => {
      if (typeof data[sortHeaderId] === 'string')
        return data[sortHeaderId].toLocaleLowerCase();
      return data[sortHeaderId];
    };
    this.dataSource.sort = this.sort;
    this.dataSource.paginator = this.paginator;
    this.dataSource.filterPredicate = 
      ((data: CertificateModel, filter: string) => {
        if(this.selectedSearchOption == this.searchOptions[0] && data.name != null)
          return data.name.toLowerCase().indexOf(filter) != -1;
        else if(this.selectedSearchOption == this.searchOptions[1] && data.customer != null)
          return data.customer.toLowerCase().indexOf(filter) != -1;
        else if(this.selectedSearchOption == this.searchOptions[2] && data.customerOrderNo != null)
          return data.customerOrderNo.toLowerCase().indexOf(filter) != -1;
        else if(this.selectedSearchOption == this.searchOptions[3] && data.customerArtNo != null)
          return data.customerArtNo.toLowerCase().indexOf(filter) != -1;
        else if(this.selectedSearchOption == this.searchOptions[4] && data.date != null)
          return data.date.toLowerCase().indexOf(filter) != -1;
      });
  }

  refreshClicked() {
    this.loadCertificates();
  }

}

  @Component({
  selector: 'app-create-cert',
  templateUrl: './create-cert.component.html',
  styleUrls: ['./create-cert.component.css'],
  encapsulation: ViewEncapsulation.None
})
export class CreateCertComponent implements OnInit {

  resultRowWrappers: ResultRowWrapper[];
  customerTextfield: string;
  customerOrderNoTextfield: string;
  customerArtNoTextfield: string;
  specificationTextfield: string;
  certificateNoTextfield: string;
  dateTextfield: string;
  deliveredQuantityTextfield: string;
  orderACKNNoTextfield: string;
  batchNumberTextfield: string;
  propertyTextfield: string;
  unitTextfield: string;
  testResultTextfield: string;
  signaturTextfield: string;
  newCertName: string;
  editedCertificate: CertificateModel
  isEditing: boolean;
  disableDeleteButton: boolean;
  titleRow0Textfield: string;
  titleRow1Textfield: string;
  titleRow2Textfield: string;
  titleRow3Textfield: string;
  selectedLogoAddressCard : LogoAddressCardModel;

  constructor(
    public previewDialog: MatDialog,
    public errorDialog: MatDialog,
    public nameDialog: MatDialog,
    public logoDIalog: MatDialog,
    private certHttpService: CertificateHttpService,
    public snackBar: MatSnackBar,
    private route: ActivatedRoute) { }

  ngOnInit() {
    console.log("On init called in create cert component!");
    
    this.selectedLogoAddressCard = {
      logo: "",
      addressRow0: "Address row 0",
      addressRow1: "Address row 1",
      addressRow2: "Address row 2",
      guid: Guid.create().toString()
    };
    this.disableDeleteButton = true;
    this.isEditing = false;
    this.resultRowWrappers = [];
    this.propertyTextfield = "";
    this.unitTextfield = "";
    this.testResultTextfield = "";
    this.route.queryParams.subscribe(params => {
      try {
        var certificate = JSON.parse(params["certificate"]);
        this.editedCertificate = certificate;
        this.fillInputFields(certificate);
        this.selectedLogoAddressCard = certificate.logoAddressCard;
      } catch{
        console.log("CATCH: No cert in params.");
        return;
      }
    });
  }

  openPreviewDialog(): void {
    var newCertificate = this.createCertificateFromInputs(Guid.create().toString());

    const dialogRef = this.previewDialog.open(PreviewDialogComponent, {
      width: '30vw',
      height: '99vh',
      disableClose: false,
      panelClass: "preview-dialog-container",
      data: newCertificate
    });

    dialogRef.updatePosition({ top: '2px' });
    dialogRef.afterClosed().subscribe(result => {
      this.snackBar.dismiss();
    });
  }

  openLogosDialog(): void {
    const dialogRef = this.logoDIalog.open(LogosDialogComponent, {
      width: '60vw',
      height: '95vh',
      disableClose: true,
      panelClass: "logo-dialog-container"
    });
    dialogRef.updatePosition({top: "10px"});
    var _this = this;
    dialogRef.componentInstance.cardSelectedEvent.subscribe((res: AppCard) => {
      _this.selectedLogoAddressCard = res.cardModel;
    });
  }

  openErrorDialog(title: string, text: string) {
    var data = {
      "title": title,
      "text": text
    };

    const dialogRef = this.errorDialog.open(ErrorDialogComponent, {
      width: '30vw',
      height: 'auto',
      disableClose: true,
      data: data
    });

    dialogRef.afterClosed().subscribe(result => {
      console.log('The dialog was closed');
    });
  }

  saveResultRowClicked() {
    var newResultRow: ResultRow = {
      property: this.propertyTextfield,
      unit: this.unitTextfield,
      testResult: this.testResultTextfield
    };
    var newResultRowWrapper: ResultRowWrapper = {
      resultRow: newResultRow,
      isChecked: false
    }
    this.resultRowWrappers.push(newResultRowWrapper);
    this.propertyTextfield = "";
    this.unitTextfield = "";
    this.testResultTextfield = "";
  }

  deleteResultRowClicked() {
    for (var i = 0; i < this.resultRowWrappers.length; i++)
      if (this.resultRowWrappers[i].isChecked) {
        this.resultRowWrappers.splice(i, 1);
        i--;
      }
  }

  saveClicked() {
    var _this = this;
    const dialogRef = this.nameDialog.open(CertNameDialogComponent, {
      width: '40vw',
      height: '37.5vh',
      disableClose: true,
      data: {
        "currentName": ""
      }
    }).componentInstance.saveClickedEvent.subscribe(res => {
      _this.newCertName = res;
      _this.saveCertificate();
    });
  }

  saveEditClicked() {
    var _this = this;
    const dialogRef = this.nameDialog.open(CertNameDialogComponent, {
      width: '40vw',
      height: '37.5vh',
      disableClose: true,
      data: {
        "currentName": this.editedCertificate.name
      }
    }).componentInstance.saveClickedEvent.subscribe(res => {
      _this.newCertName = res;
      _this.overwriteCertificate();
    });
  }

  saveCertificate() {
    var newCertificate = this.createCertificateFromInputs(Guid.create().toString());

    this.certHttpService.insertPro(newCertificate)
      .then((res => {
        this.nameDialog.closeAll();
        this.openSnackBar("Saved certificate " + "\"" + newCertificate.name + "\"", "Close", "right", "bottom", 1*3000);
      }).bind(this))
      .catch(((error) => {
        this.nameDialog.closeAll();
        this.openErrorDialog("Error", error.name + ": " + error.message);
      }).bind(this));
  }

  overwriteCertificate() {
    var newCertificate = this.createCertificateFromInputs(this.editedCertificate.guid);

    this.certHttpService.overwritePro(newCertificate)
      .then((res => {
        this.nameDialog.closeAll();
        this.openSnackBar("Saved certificate " + "\"" + newCertificate.name + "\"", "Close", "right", "bottom", 1*3000);
      }).bind(this))
      .catch(((error) => {
        this.nameDialog.closeAll();
        this.openErrorDialog("Error", error.name + ": " + error.message);
      }).bind(this));
  }

  createCertificateFromInputs(guid: string): CertificateModel {
    var resultRows: ResultRow[] = [];
    this.resultRowWrappers.forEach(e => {
      resultRows.push(e.resultRow);
    });

    var certificate: CertificateModel = {
      batchNumber: this.batchNumberTextfield,
      certificateNo: this.certificateNoTextfield,
      customer: this.customerTextfield,
      customerArtNo: this.customerArtNoTextfield,
      customerOrderNo: this.customerOrderNoTextfield,
      date: this.dateTextfield,
      deliveredQuantity: this.deliveredQuantityTextfield,
      orderACKNNo: this.orderACKNNoTextfield,
      specification: this.specificationTextfield,
      resultRows: resultRows,
      name: this.newCertName,
      signature: this.signaturTextfield,
      guid: guid,
      titleRow0: this.titleRow0Textfield,
      titleRow1: this.titleRow1Textfield,
      titleRow2: this.titleRow2Textfield,
      titleRow3: this.titleRow3Textfield,
      logoAddressCard: this.selectedLogoAddressCard
    };

    return certificate;
  }

  previewClicked() {
    this.openPreviewDialog();
    this.openSnackBar("Click outside the preview dialog to close.", "Close", "right", "bottom", 24 * 60 * 60 * 1000);
  }

  fillInputFields(certificate: CertificateModel) {
    this.isEditing = true;
    // this.openSnackBar("Editing certificate " + "\"" + certificate.name + "\"", "Close", "right", "bottom", 5 * 1000);
    this.resultRowWrappers = [];
    if (certificate.resultRows != null)
      if (certificate.resultRows.length > 0)
        certificate.resultRows.forEach(e => {
          var resultRow: ResultRowWrapper = {
            resultRow: e,
            isChecked: false
          };
          this.resultRowWrappers.push(resultRow);
        });
    this.customerArtNoTextfield = certificate.customerArtNo;
    this.customerOrderNoTextfield = certificate.customerOrderNo;
    this.customerTextfield = certificate.customer;
    this.batchNumberTextfield = certificate.batchNumber;
    this.certificateNoTextfield = certificate.certificateNo;
    this.deliveredQuantityTextfield = certificate.deliveredQuantity;
    this.dateTextfield = certificate.date;
    this.orderACKNNoTextfield = certificate.orderACKNNo;
    this.signaturTextfield = certificate.signature;
    this.specificationTextfield = certificate.specification;
    this.titleRow0Textfield = certificate.titleRow0;
    this.titleRow1Textfield = certificate.titleRow1;
    this.titleRow2Textfield = certificate.titleRow2;
    this.titleRow3Textfield = certificate.titleRow3;
    this.selectedLogoAddressCard = certificate.logoAddressCard;
  }

  openSnackBar(message: string, action: string, hPosition: string, vPosition: string, duration: number) {
    this.snackBar.open(message, action, {
      duration: duration,
      horizontalPosition: hPosition as MatSnackBarHorizontalPosition,
      verticalPosition: vPosition as MatSnackBarVerticalPosition
    });
  }

  checkBoxClickedEvent() {
    var areAnyChecked = false;
    this.resultRowWrappers.forEach(e => {
      if (e.isChecked) {
        areAnyChecked = true;
        return;
      }
    });
    this.disableDeleteButton = !areAnyChecked;
  }

  selectCardClicked() {
    this.openLogosDialog();
  }

}

导航后停止工作的已用 http 服务的代码:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { LogoAddressCardModel } from 'src/data_models/LogoAddressCardModel';
import { RequestOptions } from '@angular/http';

@Injectable({
  providedIn: 'root'
})
export class LogoAddressCardHttpServiceService {

  constructor(private httpClient: HttpClient) { }

  getAllObs(): Observable<LogoAddressCardModel[]> {
    return this.httpClient.get<LogoAddressCardModel[]>("http://localhost:50219/api/logo_address_card/get_all");
  }

  async getAllPro(): Promise<LogoAddressCardModel[]> {
    var response = await this.getAllObs();
    return response.toPromise();
  }

  insertObs(model: LogoAddressCardModel): Observable<any> {
    console.log(model.addressRow0);
    console.log(model.addressRow1);
    console.log(model.addressRow2);

    return this.httpClient.post<any>("http://localhost:50219/api/logo_address_card/insert", model);
  }

  async insertPro(model: LogoAddressCardModel): Promise<any> {
    var response = await this.insertObs(model);
    return response.toPromise();
  }

  overwriteObs(model: LogoAddressCardModel): Observable<any> {
    return this.httpClient.post<any>("http://localhost:50219/api/logo_address_card/overwrite", model);
  }

  async overwritePro(model: LogoAddressCardModel): Promise<any> {
    var response = await this.overwriteObs(model);
    return response.toPromise();
  }

  deleteObs(model: LogoAddressCardModel): Observable<any> {
    return this.httpClient.post<any>("http://localhost:50219/api/logo_address_card/delete", model);
  }

  async deletePro(model: LogoAddressCardModel): Promise<any> {
    var response = await this.deleteObs(model);
    return response.toPromise();
  }

}

为什么相同的 http 服务和 http 调用方法先起作用而后不起作用?我完全感到困惑,感觉想把自己的头发扯掉。

标签: angular

解决方案


使用 NavigationExtras 的问题是它们在发出 HTTP 请求时被添加到 URL。这就是导致错误请求的原因。API 控制器在它没有预料到的 URL 中得到了一堆参数。


推荐阅读