我正在编写 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],只是为了确保这不是导致它的原因。不,那里也没有任何线索。那么什么可能导致这种情况呢?有什么想法吗?我应该在哪里调试/寻找才能找到这个问题的确切根源?我需要一些帮助,已经连续两天被这个问题困扰了!
如果您需要更多代码片段(互连)来分析场景,请告诉我。很乐意分享。
感谢期待!
我解决了这件事。经过仔细检查,我在 Registration.component.ts 文件中找到了错误来源,在我初始化 regform 表单组内的字段的部分中。
我用过这条线
subType:['', Validators.required]
其实应该是
subscriptionType:['', Validators.required]
因为在我的
RegistrationViewModel.cs
文件中,各自的字段名称是SubscriptionType。字段名称异常导致发布响应失败。
现在一切正常。细节问题!