Обработка ошибок проверки API в Blazor WebAssembly - PullRequest
4 голосов
/ 07 мая 2020

Я изучаю 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."
        ]
    }
}

Обратите внимание, что ошибка проверки находится в массиве «errors» в JSON.

Вернувшись в приложение Blazor Client, у меня есть типичный Функция 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?
    }
}

Мой вопрос: как лучше всего обрабатывать ошибки проверки на стороне сервера? Взаимодействие с пользователем должно быть таким же, как и при любой другой ошибке проверки, с выделенным полем, отображаемым сообщением проверки и сводкой вверху страницы.

Ответы [ 2 ]

2 голосов
/ 09 мая 2020

Я решил эту проблему, создав компонент ServerValidator. Я опубликую здесь код, если он будет полезен другим, ищущим решение той же проблемы.

Этот код предполагает, что вы вызываете конечную точку веб-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; }
    }
}

Чтобы использовать этот новый компонент, вам нужно будет добавить компонент в вашу EditForm:

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

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

</EditForm>

Наконец, вы можете запустить проверку в разделе @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());
        }
    }

}

Теоретически это должно позволить вам полностью обойти проверку клиента и полагаться в этом на ваш веб-API. На практике я обнаружил, что Blazor выполняет проверку клиента, когда в вашей модели есть аннотации, даже если вы не включили <DataAnnotationsValidator /> в свою форму. Однако он по-прежнему будет обнаруживать любые проблемы с проверкой на сервере и возвращать их вам.

1 голос
/ 07 мая 2020

как лучше всего обрабатывать ошибки проверки на стороне сервера? Пользовательский интерфейс должен быть таким же, как и при любой другой ошибке проверки, с выделенным полем, отображаемым сообщением проверки и сводкой вверху страницы.

Я не знаю, что происходит в вашем response, поэтому я сделал общую c версию компонента, который делает то, что вам нужно.

  1. Получить CascadingParameter EditContext

    [CascadingParameter]
    public EditContext EditContext { get; set; }
    
  2. Имейте ValidationMessageStore для хранения ошибок и функцию, которая будет отображать ошибки

    private ValidationMessageStore _messageStore;
    
    private EventHandler<ValidationRequestedEventArgs> OnValidationRequested => (s, e) =>
        {
            _messageStore.Clear();
        };
    private EventHandler<FieldChangedEventArgs> OnFieldChanged => (s, e) =>
        {
            _messageStore.Clear(e.FieldIdentifier);
        };
    
    protected override void OnInitialized()
    {
        base.OnInitialized();
    
        if (EditContext != null)
        {
            _messageStore = new ValidationMessageStore(EditContext);
            EditContext.OnFieldChanged += OnFieldChanged;
            EditContext.OnValidationRequested += OnValidationRequested;
        }
    }
    
    public override void Dispose()
    {
        base.Dispose();
    
        if (EditContext != null)
        {
            EditContext.OnFieldChanged -= OnFieldChanged;
            EditContext.OnValidationRequested -= OnValidationRequested;
        }
    }
    
    private void AddFieldError(ERROR_CLASS_YOU_ARE_USING validatorError)
    {
        _messageStore.Add(EditContext.Field(validatorError.FIELD_NAME), validatorError.ERROR_MESSAGE);
    }
    
  3. Вызовите функцию компонента, используя его ref

    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?
    
            // You could also have a foreach or a function that receives an List for multiple fields error display
            MyHandleErrorComponent.AddFieldError(response.ERROR_PROPERTY);
        }
    }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...