处理 Blazor WebAssembly 中的 API 验证错误

Rog*_*MKE 8 asp.net-core-webapi blazor

我正在学习 Blazor,并且我有一个 WebAssembly 客户端应用程序。

我在服务器上创建了一个 WebAPI,它在标准数据注释验证之外做了一些额外的验证。例如,当它尝试将记录写入数据库时​​,它会检查是否不存在具有相同电子邮件地址的其他记录。某些类型的验证不能在客户端可靠地发生,特别是在竞争条件可能产生不良结果的情况下。

API 控制器向客户端返回一个ValidationProblem结果,Postman 将结果的正文显示为:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|f06d4ffe-4aa836b5b3f4c9ae.",
    "errors": {
        "Email": [
            "The email address already exists."
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,验证错误位于 JSON 中的“errors”数组中。

回到 Blazor 客户端应用程序,我有一个典型的 HandleValidSubmit 函数,它将数据发布到 API 并接收响应,如下所示:

private async void HandleValidSubmit()
{
    var response = await Http.PostAsJsonAsync<TestModel>("api/Test", testModel);

    if (response.StatusCode != System.Net.HttpStatusCode.Created)
    {
        // How to handle server-side validation errors?
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,如何最好地处理服务器端验证错误?用户体验应该与任何其他验证错误相同,突出显示该字段,显示验证消息,以及页面顶部的摘要。

Rog*_*MKE 8

我最终通过创建一个 ServerValidator 组件解决了这个问题。我会在此处发布代码,以防其他人寻求解决相同问题的方法。

此代码假定您正在调用 Web API 端点,如果出现问题,该端点将返回 ValidationProblem 结果。

 public class ServerValidator : ComponentBase
 {
    [CascadingParameter]
    EditContext CurrentEditContext { get; set; }

    protected override void OnInitialized()
    {
        base.OnInitialized();

        if (this.CurrentEditContext == null)
        {
            throw new InvalidOperationException($"{nameof(ServerValidator)} requires a cascading " +
                $"parameter of type {nameof(EditContext)}. For example, you can use {nameof(ServerValidator)} " +
                $"inside an EditForm.");
        }
    }

    public async void Validate(HttpResponseMessage response, object model)
    {
        var messages = new ValidationMessageStore(this.CurrentEditContext);

        if (response.StatusCode == HttpStatusCode.BadRequest)
        {
            var body = await response.Content.ReadAsStringAsync();
            var validationProblemDetails = JsonSerializer.Deserialize<ValidationProblemDetails>(body);

            if (validationProblemDetails.Errors != null)
            {
                messages.Clear();

                foreach (var error in validationProblemDetails.Errors)
                {
                    var fieldIdentifier = new FieldIdentifier(model, error.Key);
                    messages.Add(fieldIdentifier, error.Value);
                }
            }
        }

        CurrentEditContext.NotifyValidationStateChanged();
    }

    // This is to hold the response details when the controller returns a ValidationProblem result.
    private class ValidationProblemDetails
    {
        [JsonPropertyName("status")]
        public int? Status { get; set; }

        [JsonPropertyName("title")]
        public string Title { get; set; }

        [JsonPropertyName("type")]
        public string Type { get; set; }

        [JsonPropertyName("errors")]
        public IDictionary<string, string[]> Errors { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用这个新组件,您需要在 EditForm 中添加该组件:

<EditForm Model="agency" OnValidSubmit="HandleValidSubmit">
        <ServerValidator @ref="serverValidator" />
        <ValidationSummary />

        ... put all your form fields here ...

</EditForm>
Run Code Online (Sandbox Code Playgroud)

最后,您可以在您的@code部分开始验证:

@code {
    private TestModel testModel = new TestModel();
    private ServerValidator serverValidator;

    private async void HandleValidSubmit()
    {
        var response = await Http.PostAsJsonAsync<TestModel>("api/TestModels", testModel);

        if (response.StatusCode != System.Net.HttpStatusCode.Created)
        {
            serverValidator.Validate(response, testModel);
        }
        else
        {
            Navigation.NavigateTo(response.Headers.Location.ToString());
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

从理论上讲,这应该允许您完全绕过客户端验证并依靠您的 Web API 来完成。在实践中,我发现 Blazor 会在您的模型上有注释时执行客户端验证,即使<DataAnnotationsValidator />您的表单中没有包含 a 。但是,它仍然会在服务器上捕获任何验证问题并将它们返回给您。