[.NET] Как изменить формат данных JSON на Snake Case в ASP.NET Core Web API

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
20-Фев-2021 18:30

Стандартный способ отображения данных в ASP.NET Web API - это Camel Case. Но иногда возникают задачи, когда нужно изменить формат данных на нечто другое. Например, на фронтенде у вас может быть SPA, которое как раз работает с данными в формате snake case. В этой статье я покажу, как изменить формат сериализации в ASP.NET Core Web API.
Camel case vs Snake caseВ статье приведены примеры кода, которые необходимо будет перенести в свой проект. В конце поста - ссылка на Github репозиторий, где я уже настроил приложение на сериализацию в snake case. Все примеры кода и проект в репозитории написаны на ASP.NET Core версии .net5.Меняем формат сериализации запросов и ответов сервераВсе, что нам нужно сделать для изменения сериализации, это установить Naming Policy в настройках приложения. Стандартный полиси - это Camel Case. Установка полиси на Snake Case - задача несложная.Сначала добавим утилитарные методы для трансформации строк к snake case:
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Utils.Helpers;
namespace YourNamespace
{
    public static class JsonSerializationExtensions
    {
        private static readonly SnakeCaseNamingStrategy _snakeCaseNamingStrategy
            = new SnakeCaseNamingStrategy();
        private static readonly JsonSerializerSettings _snakeCaseSettings = new JsonSerializerSettings
        {
            ContractResolver = new DefaultContractResolver
            {
                NamingStrategy = _snakeCaseNamingStrategy
            }
        };
        public static string ToSnakeCase(this T instance)
        {
            if (instance == null)
              {
                   throw new ArgumentNullException(paramName: nameof(instance));
               }
            return JsonConvert.SerializeObject(instance, _snakeCaseSettings);
        }
        public static string ToSnakeCase(this string @string)
        {
            if (@string == null)
              {
                   throw new ArgumentNullException(paramName: nameof(@string));
               }
            return _snakeCaseNamingStrategy.GetPropertyName(@string, false);
        }
    }
}
Здесь мы добавляем пару полезных методов: первая перегрузка метода нам пригодится для применения на объектах, вторая - для строкового значения. Мы используем тут класс SnakeCaseNamingStrategy для трансформации строк. Эти методы понадобятся нам в реализации нашего Naming Policy: 
using System.Text.Json;
using Utils.Serialization;
namespace YourNamespace
{
    public class SnakeCaseNamingPolicy : JsonNamingPolicy
    {
        public override string ConvertName(string name) => name.ToSnakeCase();
    }
}
Здесь мы как раз используем метод-экстеншн ToSnakeCase() для трансформации. Инстанс класса SnakeCaseNamingPolicy мы будем использовать в Startup.cs в методе ConfigureServices:
public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    // ...
    services
        .AddMvc()
        .AddJsonOptions(x =>
        {
            x.JsonSerializerOptions.PropertyNamingPolicy = new SnakeCaseNamingPolicy();
        });
    // ...
  }
}
Несмотря на то, что у нас Web API, мы используем метод .AddMvc() вместо .AddControllers(), так как у последнего нет возможности переопределять способ сериализации. Работать наш Web API будет и в рамках MVC.После добавления этой настройки наше приложение принимает и отдает данные в JSON в формате Snake Case:
Данные в Snake Case форматеНо когда у нас возникнет ошибка валидация входящих данных, мы обнаружим, что…
Формат выдачи ошибок валидации до сих пор в Camel CaseСкрин выше - это то, как будет отображена ошибка валидации. Как мы видим, она до сих пор в формате Camel Case, даже с примененными нами настройками. Более того, поля класса FirstName и LastName написаны уже в формате Pascal Case, а мы принимаем и отдаем их в Snake Case. Нам такое поведение не подходит, будем исправлять.Меняем формат выдачи ошибок валидацииЧтобы это сделать, нам нужно заменить стандартную "фабрику ответов" в ASP на свою собственную. Сначала создадим класс, который и будет формировать нам структуру ошибок:
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Mvc;
namespace YourNamespace
{
    public class ValidationProblemDetails : ProblemDetails
    {
        // 400 status ccode is usually used for input validation errors
        public const int ValidationStatusCode = (int)HttpStatusCode.BadRequest;
        public ValidationProblemDetails(ICollection validationErrors)
        {
            ValidationErrors = validationErrors;
            Status = ValidationStatusCode;
            Title = "Request Validation Error";
        }
        public ICollection ValidationErrors { get; }
        public string RequestId => Guid.NewGuid().ToString();
    }
}
Этот класс принимает список ошибок, который и будет отображен в JSON. Класс наследуется от ProblemDetails из неймспейса Microsoft.AspNetCore.Mvc. Поле RequestId нам поможет найти конкретную запись ошибки в логах при просмотре через наш UI мониторинга.Затем нам нужно создать класс для замены стандартного InvalidModelStateResponseFactory:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Utils.Serialization;
namespace YourNamespace
{
    public class ValidationProblemDetailsResult : IActionResult
    {
        public async Task ExecuteResultAsync(ActionContext context)
        {
            var modelStateEntries = context.ModelState
                .Where(e => e.Value.Errors.Count > 0)
                .ToArray();
            var errors = new List();
            if (modelStateEntries.Any())
            {
                foreach (var (key, value) in modelStateEntries)
                {
                    errors.AddRange(value.Errors
                        .Select(modelStateError => new ValidationError(
                            name: key.ToSnakeCase(),
                            description: modelStateError.ErrorMessage)));
                }
            }
            await new JsonErrorResponse(
                context: context.HttpContext,
                error: new ValidationProblemDetails(errors),
                statusCode: ValidationProblemDetails.ValidationStatusCode).WriteAsync();
        }
    }
}
Напоследок добавим замену механизма формирования ошибок в классе Startup.cs:
public class Startup
{
   // ...
  public void ConfigureServices(IServiceCollection services)
  {
    // ...
    services
        .Configure(x =>
        {
            x.InvalidModelStateResponseFactory = ctx => new ValidationProblemDetailsResult();
        });
    // ...
  }
}
И теперь наши ошибки сериализуются в Snake Case тоже:
Структура ошибки в формате Snake CaseПосле всех изменений наше приложение теперь не только отдает и принимает JSON данные в формате Snake Case, но и показывает валидационные ошибки тоже в том виде, в котором нам нужно. Здесь по ссылке вы можете открыть Github репозиторий, где есть пример настроенного приложения. По описанным шагам вы можете применить не только Snake Case, но и любой другой формат сериализации данных, который вам по душе.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_.net, #_.net, #_json, #_asp.net_core, #_tutorial, #_.net
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 09:43
Часовой пояс: UTC + 5