Как обрабатывать ошибки внутри пользовательских JsonConverters в ядре asp net? - PullRequest
0 голосов
/ 20 марта 2020

Я использовал net core 3.1 в своем проекте веб-API. Чтобы принять даты в формате dd-MM-гггг от пользователя в теле запроса JSON, я создал преобразователь DateTime, который преобразует все входящие и исходящие даты в указанный формат.

Ниже представлен мой конвертер DateTime:

public class DateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Debug.Assert(typeToConvert == typeof(DateTime));
        return DateTime.Parse(reader.GetString());
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString("dd-MM-yyyy hh:mm tt"));
    }
}

И я зарегистрировал то же самое в файле startup.cs, например:

services.AddControllers(options =>
        {
            //To accept browser headers.
            options.RespectBrowserAcceptHeader = true;
        }).
        AddNewtonsoftJson(options =>
        {
            // Use the default property (Pascal) casing
            options.SerializerSettings.ContractResolver = new DefaultContractResolver();
            options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;

        }).
        AddJsonOptions(options =>
         {
             //converter to accept date in particular(dd-MM-yyyy) format
             options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
         });

Теперь я сталкиваюсь с некоторыми проблемами в конвертере DateTime. Я создал фильтр действий для проверки ошибок ModelState, которые вызываются после возникновения исключения из конвертера DateTime. Ниже приведен фильтр ошибок ModelState:

    public override void OnActionExecuting(ActionExecutingContext context)
    {

        if (!context.ModelState.IsValid)
        {

            ApiResponseModel<IEnumerable<ValidationErrorModel>> apiResponseModel = new ApiResponseModel<IEnumerable<ValidationErrorModel>>();

            apiResponseModel.ResponseCode = (int)HttpStatusCode.BadRequest;
            apiResponseModel.ResponseMessage = string.Join(" ",
                context.ModelState.Values.Where(E => E.Errors.Count > 0)
                .SelectMany(E => E.Errors)
                .Select(E => E.ErrorMessage)
                .ToArray());
            apiResponseModel.ResponseData = null;
            context.Result = new OkObjectResult(apiResponseModel);
        }
    }

Когда дата имеет неправильный формат, например, в формате MM-dd-YYYY, она генерирует ошибку ModelState и выдает свое встроенное сообщение об ошибке как «Предоставленное значение». является недействительным". Я хотел отобразить другое сообщение вместо этого встроенного сообщения, что-то вроде «{nameofdatetimefield} не в правильном формате». Итак, как я могу добиться того же внутри этого DateTimeConverter .?

1 Ответ

0 голосов
/ 21 марта 2020

В вашем конвертере вместо использования API, подобного DateTime.Parse, который генерирует исключение для ввода в недопустимом формате, вы можете использовать метод TryParse ИЛИ TryParseExact и вызвать исключение самостоятельно с другим сообщением об ошибке. , Я предполагаю, что вы все еще хотите, чтобы тип исключения был таким же (т. Е. FormatException).

public override DateTime Read(
    ref Utf8JsonReader reader,
    Type typeToConvert,
    JsonSerializerOptions options)
{
    if (DateTime.TryParseExact(
        reader.GetString(),
        "dd-MM-yyyy hh:mm tt",
        CultureInfo.InvariantCulture,
        DateTimeStyles.None, 
        out DateTime date))
    {
        return date;
    }
    throw new FormatException("A DateTime property is not in a proper format.");
}

Если вы хотите получить доступ к самому имени свойства в конвертере (для вставки в сообщение об исключении или по любой другой причине), , что нелегко выполнить . Если вам действительно нужно, один из способов добиться этого - создать преобразователь для сериализуемого родительского типа со свойством DateTime и получить к нему доступ таким образом. Этот подход не идеален, особенно если у вас есть такие свойства DateTime во многих различных родительских типах (так как для этого вам потребуется создать собственные конвертеры). Несмотря на это, в качестве примера приведен фрагмент, использующий типичный родительский тип WeatherForecast, содержащий DateTime, который поставляется с шаблоном dotnet new webapi:

// The "Read" side of a JsonConverter<WeatherForecast> implementation

public override WeatherForecast Read(
    ref Utf8JsonReader reader,
    Type typeToConvert,
    JsonSerializerOptions options)
{
    var forecast = new WeatherForecast();

    while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
    {
        if (reader.ValueTextEquals(nameof(forecast.Date)))
        {
            if (!reader.Read())
            {
                throw new JsonException();
            }

            if (DateTime.TryParseExact(
                        reader.GetString(),
                        "dd-MM-yyyy hh:mm tt",
                        CultureInfo.InvariantCulture,
                        DateTimeStyles.None, 
                        out DateTime date))
            {
                forecast.Date = date;
            }
            else
            {
                throw new FormatException(
                    $"The {nameof(forecast.Date)} is not in a proper format.");
            }
        }

        // Add logic for creating the rest of the object properties from the JSON.
    }

    return forecast;
}
...