Angular18 客户端请求在提交表单后给出 HttpErrorResponse 而不是点击 API 端点 -

问题描述 投票:0回答:1

我正在编写 ASP.NET Core 8 Web API 和 Angular 18 全栈应用程序。目前正在测试我来自客户端应用程序的请求是否到达后端 API 端点。我有一个注册页面,我在其中填写所需的详细信息并单击“提交”。我预计 API 端点方法中的断点会被命中。那没有发生。

我安慰了我应该收到的回复,发现我收到了

HttpResponseError
。我还收到了
net::ERR_CONNECTION_REFUSED
错误消息。我稍后会在帖子中列出详细信息。让我首先发布客户端应用程序和后端 API 的相关代码,以便您可以检查我到目前为止所尝试的内容。

AuthController.cs

[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    private readonly UserManager<AppUser> _userManager;
    private readonly IConfiguration _configuration;

    public AuthController(UserManager<AppUser> userManager, IConfiguration configuration)
    {
        _userManager = userManager;
        _configuration = configuration;
    }

    [HttpPost("samplemethod")]
    [AllowAnonymous]
    public ActionResult<string> SampleMethod([FromBody]RegisterViewModel viewModel)
    {
        if (viewModel != null)
        {
            return Ok(new AuthResponseViewModel
                {
                    Token = "blablabla",
                    Result = true,
                    Message = "Received data from client app!"
                });
        }
        else
        {
            return BadRequest(new AuthResponseViewModel
                {
                    Token = " ",
                    Result = false,
                    Message = "Did not receive data from client app!"
                });
        }
    }
}

RegisterViewModel.cs

public class RegisterViewModel
{
    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;

    [Required]
    public string FullName { get; set; } = string.Empty;

    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; } = string.Empty;

    [Required]
    [DataType(DataType.Password)]
    public string ConfirmPassword { get; set; } = string.Empty;

    [Required]
    public string MobileNumber { get; set; } = string.Empty;

    [Required]
    public string SubscriptionType { get; set; } = string.Empty;

    [Required]
    public string Role { get; set; } = string.Empty;
}

AuthResponseViewModel.cs

public class AuthResponseViewModel
{
    public string Token { get; set; } = string.Empty;
    public bool Result { get; set; }
    public string Message { get; set; } = string.Empty;
}

Program.cs

using ForSideSystems.API.Data;
using ForSideSystems.API.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.SqlServer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text;
using ForSideSystems.API.Extensions;


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

builder.Services.AddSwaggerExplorer()
                .InjectDbContext(builder.Configuration)
                .AddAppConfig(builder.Configuration)
                .AddIdentityHandlersAndStores()
                .AddIdentityAuth(builder.Configuration);

var app = builder.Build();

app.ConfigureCORS(builder.Configuration)
   .ConfigureSwaggerExplorer()
   .AddIdentityAuthMiddlewares();

app.UseDefaultFiles();
app.UseStaticFiles();


app.MapControllers();

app.Run();

AppConfigExtensions.cs
: *** 包含 CORS 配置

public static class AppConfigExtensions
{
    public static WebApplication ConfigureCORS(this WebApplication app, IConfiguration config) 
    {
        app.UseCors(t =>
        t.WithOrigins("http://localhost:4200")
        .AllowAnyMethod()
        .AllowAnyHeader());

        return app;
    }

    public static IServiceCollection AddAppConfig(this IServiceCollection services, IConfiguration config)
    {
        services.Configure<AuthSettings>(config.GetSection("AuthSettings"));
        return services;
    }
}

现在是我的 Angular 18 客户端应用程序的代码块。

auth.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';


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

  constructor(private http:HttpClient) { }

  authURL = 'http://localhost:5254/api/auth';

  createUser(formData:any){
    return this.http.post(this.authURL+'/register', formData);
  }

  createSampleResponse(formData:any)
  {
    console.log("sending request to backend api");
    return this.http.post(this.authURL+'/samplemethod', formData);
  }
  
}

registration.component.html -

<div class="mb-4 h2 text-success">
    Sign up
</div>

<form [formGroup]="regform" (ngSubmit)="onSubmit()">
    <div class="mb-3">
        <input class="form-control bg-body-secondary" placeholder="Email" formControlName="email">
        <div class="error-feedback" *ngIf="hasDisplayableError('email')">
          <div *ngIf="regform.controls.email.hasError('required')">
            Please enter email address.
          </div>
          <div *ngIf="regform.controls.email.hasError('email')">
            Please enter a valid email address.
          </div>
        </div>
      </div>

    <div class="mb-3">
        <input class="form-control bg-body-secondary" placeholder="Full Name" formControlName="fullName">
        <div class="error-feedback" *ngIf="hasDisplayableError('fullName') && regform.controls.fullName.hasError('required')">
          Please enter full name.
        </div>
    </div>

    <div class="mb-3">
        <input class="form-control bg-body-secondary" placeholder="Password" formControlName="password">
        <div class="error-feedback" *ngIf="hasDisplayableError('password')">
          <ng-container [ngSwitch]="regform.controls.password.errors | firstKey">
            <div *ngSwitchCase="'required'">
              Please enter password.
            </div>
            <div *ngSwitchCase="'minlength'">
              Atleast 6 characters required.
            </div>
            <div *ngSwitchCase="'pattern'">
              One or more special character(s).
            </div>
          </ng-container>
          
        </div>
    </div>

    <div class="mb-3">
        <input class="form-control bg-body-secondary" placeholder="Confirm Password" formControlName="confirmPassword">
        <div class="error-feedback" 
        *ngIf="hasDisplayableError('confirmPassword') && regform.controls.confirmPassword.hasError('passwordMismatch')">
          Passwords doesn't match.
        </div>
    </div>

    <div class="mb-3">
      <input class="form-control bg-body-secondary" placeholder="Mobile Number" formControlName="mobileNumber">
      <div class="error-feedback"
      *ngIf="hasDisplayableError('mobileNumber') && regform.controls.mobileNumber.hasError('required')">
        Please provide 10 digit mobile number
      </div>
    </div>

    <div class="mb-3">
      <select class="form-control bg-body-secondary" formControlName="subType" (change)="changeSubscription($event)">
        <option value="">-- Choose Subscription Type --</option>
        <option value="Monthly">Monthly</option>
        <option value="Quarterly">Quarterly</option>
        <option value="Yearly">Yearly</option>
      </select>
      <div class="error-feedback" *ngIf="isSubmitted && f.subType.errors">
        <div *ngIf="f.subType.errors.required">Please select Subscription Type</div>
      </div>
    </div>

    <div class="mb-3">
      <select class="form-control bg-body-secondary" formControlName="role" (change)="changeRole($event)">
        <option value="">-- Choose Role --</option>
        <option value="Actor">Actor</option>
        <option value="Actor">Painter</option>
        <option value="Actor">Photographer</option>
        <option value="Actor">Musician</option>
      </select>
      <div class="error-feedback" *ngIf="isSubmitted && f.role.errors">
        <div *ngIf="f.role.errors.required">Please select Role Type</div>
      </div>
    </div>
    
    <div class="mt-4">
        <button type="submit" class="btn btn-success w-100 rounded-3">
          Register
        </button>
    </div>
</form>

registration.component.ts -

import { Component } from '@angular/core';
import { ToastrService } from 'ngx-toastr';
import { CommonModule } from '@angular/common';
import { AbstractControl, FormBuilder, ReactiveFormsModule, ValidatorFn, Validators } from '@angular/forms';
import { FirstKeyPipe } from '../../shared/pipes/first-key.pipe';
import { AuthService } from '../../shared/services/auth.service';

@Component({
  selector: 'app-registration',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule, FirstKeyPipe],
  templateUrl: './registration.component.html',
  styles: ``
})

export class RegistrationComponent {

  /**
   *
   */
  constructor(public formBuilder: FormBuilder, private toastr: ToastrService, private service: AuthService,) {

  }

  isSubmitted: boolean = false;

  passwordMatchValidator: ValidatorFn = (control: AbstractControl): null => {
    const password = control.get('password')
    const confirmPassword = control.get('confirmPassword')

    if (password && confirmPassword && password.value != confirmPassword.value)
      confirmPassword?.setErrors({ passwordMismatch: true })
    else
      confirmPassword?.setErrors(null)

    return null;
  }

  regform = this.formBuilder.group({
    fullName: ['', Validators.required],
    email: ['', [Validators.required, Validators.email]],
    password: ['', [
      Validators.required,
      Validators.minLength(6),
      Validators.pattern(/(?=.*[^a-zA-Z0-9 ])/)]],
    confirmPassword: [''],
    mobileNumber:['', Validators.required],
    subType:['', Validators.required],
    role:['', Validators.required]
  }, { validators: this.passwordMatchValidator })

  hasDisplayableError(controlName: string): Boolean {
    const control = this.regform.get(controlName);
    return Boolean(control?.invalid) &&
      (this.isSubmitted || Boolean(control?.touched)|| Boolean(control?.dirty))
  }

  onSubmit()
  {
    this.isSubmitted=true;

    if(this.regform.valid)
    {
      this.service.createSampleResponse(this.regform.value)
        .subscribe({
          next:(res:any)=>
          {
            if(res.result)
            {
              this.regform.reset();
              this.isSubmitted = false;

              this.toastr.success('Response received from server backend!', 'Response')
            }
          },
          error:err=>
          {
            console.log('error:', err);
          }
        })
    }
  }

  get f() {
    return this.regform.controls;
  }

  changeSubscription(e:any) {
    this.regform.controls.subType.setValue(e.target.value, {
      onlySelf: true
    })
  }

  changeRole(e:any)
  {
    this.regform.controls.role.setValue(e.target.value, {
      onlySelf: true
    })
  }

}

first-key.pipe.ts - ** 为反应式表单中的输入验证创建自定义管道

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'firstKey',
  standalone: true
})
export class FirstKeyPipe implements PipeTransform {

  transform(value: any): string | null {
    const keys = Object.keys(value);
    if (keys && keys.length > 0)
      return keys[0];
    return null;
  }

}

最后我在控制台上收到的错误响应 -

registration.component.ts:65 
 POST http://localhost:5254/api/auth/samplemethod 400 (Bad Request)

registration.component.ts:78 error: 
HttpErrorResponse {headers: _HttpHeaders, status: 400, statusText: 'Bad Request', url: 'http://localhost:5254/api/auth/samplemethod', ok: false, …}
error
: 
errors
: 
{SubscriptionType: Array(1)}
status
: 
400
title
: 
"One or more validation errors occurred."
traceId
: 
"00-634b9f748dc9bdc47bc227a19c2ddf7a-596e55061b94d743-00"
type
: 
"https://tools.ietf.org/html/rfc9110#section-15.5.1"
[[Prototype]]
: 
Object
headers
: 
_HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message
: 
"Http failure response for http://localhost:5254/api/auth/samplemethod: 400 Bad Request"
name
: 
"HttpErrorResponse"
ok
: 
false
status
: 
400
statusText
: 
"Bad Request"
url
: 
"http://localhost:5254/api/auth/samplemethod"
[[Prototype]]
: 
HttpResponseBase



POST http://localhost:5254/api/auth/samplemethod 
net::ERR_CONNECTION_REFUSED

就是这样。错误堆栈显示“发生一个或多个验证错误。”但我确信我正在从前端发送正确的经过验证的数据。所以这没有意义。我很困惑。我的客户端应用程序是 Angular18 独立类型应用程序。我尝试从“SampleMethod”api 端点添加和删除 [FromBody],只是为了确保这不是导致它的原因。不,那里也没有任何线索。那么什么可能导致这种情况呢?有什么想法吗?我应该在哪里调试/寻找才能找到这个问题的确切根源?我需要一些帮助,已经连续两天被这个问题困扰了!

如果您需要更多代码片段(互连)来分析场景,请告诉我。很乐意分享。

感谢期待!

asp.net-core-webapi http-error angular-fullstack asp.net-core-8 angular18
1个回答
0
投票

我解决了这件事。经过仔细检查,我在 Registration.component.ts 文件中找到了错误来源,在我初始化 regform 表单组内的字段的部分中。

我用过这条线

subType:['', Validators.required]

其实应该是

subscriptionType:['', Validators.required]

因为在我的

RegistrationViewModel.cs
文件中,各自的字段名称是SubscriptionType。字段名称异常导致发布响应失败。

现在一切正常。细节问题!

© www.soinside.com 2019 - 2024. All rights reserved.