Обработать исключение привязки модели localdate ядра asp.net - PullRequest
0 голосов
/ 27 декабря 2018

Я намеревался вернуть код состояния 400 с сервера, когда полезная нагрузка сообщения имеет недопустимый формат или данные.Однако при отправке значения xyz123 для поля даты сервер возвращает код состояния 500.Исключение:

NodaTime.Text.UnparsableValueException: The value string does not match the required number from the format string "uuuu". Value being parsed: '^xyz123'. (^ indicates error position.)
   at NodaTime.Text.ParseResult`1.GetValueOrThrow() in C:\Users\jon\Test\Projects\nodatime\build\tzdbupdate\tmp-2.4\nodatime\src\NodaTime\Text\ParseResult.cs:line 81
   at NodaTime.Serialization.JsonNet.NodaConverterBase`1.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 2159
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 1032
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 2386
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Formatters.JsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)

Я ожидал, что исключение будет перехвачено и помещено в словарь ModelState.Есть ли способ внедрить это поведение (то есть перехват исключений) в JsonInputFormatter или BodyModelBinder, чтобы добавить их в ModelState вместо того, чтобы они нарушали привязку модели?

В качестве обходного пути я использую промежуточное программное обеспечение для обработки ошибок дляпоймать FormatException.На данный момент у меня, однако, больше нет контекстной информации, такой как JPATH, чтобы помочь пользователю или UI-клиенту.

Возможные причины?:

  • Ошибка десериализации NodaTime, JsonInputFormatter, кажется, используетjsonSerializer.Error и prent throwing JsonException (но используются FormatException)
  • Ошибка в десериализации ядра asp.net, должна обрабатывать FormatException
  • Неправильная конфигурация на моей стороне

Модульный тест для воспроизведения поведения

public class ModelBindingErrorTest
{
    private class ModelBindingErrorStartup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            var mvcBuilder = services.AddMvc();
            services.Configure<MvcJsonOptions>(options => options.SerializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb));
            //var mvcBuilder = services.AddMvcCore();
            //mvcBuilder.AddJsonFormatters(jsonSerializerSettings => jsonSerializerSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb));
            mvcBuilder.AddApplicationPart(typeof(ModelBindingErrorStartup).Assembly);
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc();
        }
    }

    [Fact]
    public async Task Test()
    {
        var message = "The value string does not match the required number from the format string \"uuuu\". Value being parsed: \'^123xyz\'. (^ indicates error position.)";
        try
        {
            var client = new TestServer(new WebHostBuilder().UseStartup<ModelBindingErrorStartup>()).CreateClient();
            var response = await client.PostAsync(new Uri("/MainController/LocalDate", UriKind.Relative), new StringContent("{'Birthday':'123xyz'}", Encoding.UTF8, MediaTypeNames.Application.Json));
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);
            var body = await response.Content.ReadAsStringAsync();
            Assert.Equal(message, body);
        }
        catch (UnparsableValueException e)
        {
            Assert.Equal(message, e.Message);
            // The Test Fails Here
            Assert.False(true);
        }
    }
}

[Route("/MainController")]
public class MainController : Controller
{
    [HttpPost("LocalDate")]
    public object LocalDate([FromBody]Model model)
    {
        return ModelState["Birthday"].Errors[0].Exception.Message;
    }
}

public class Model
{
    public LocalDate Birthday { get; set; }
}

Использованные пакеты

  • Microsoft.AspNetCore.TestHost 2.2.0
  • Microsoft.NET.Test.Sdk 15.9.0
  • Newtonsoft.Json 12.0.1
  • NodaTime 2.4.2
  • NodaTime.Serialization.JsonNet 2.0.0
  • Swashbuckle.NodaTime.AspNetCore 2.0.0
  • xunit 2.4.1
  • xunit.runner.visualstudio 2.4.1
...