Стандартный способ отображения данных в ASP.NET Web API — это Camel Case. Но иногда возникают задачи, когда нужно изменить формат данных на нечто другое. Например, на фронтенде у вас может быть SPA, которое как раз работает с данными в формате snake case. В этой статье я покажу, как изменить формат сериализации в ASP.NET Core Web API.
В статье приведены примеры кода, которые необходимо будет перенести в свой проект. В конце поста — ссылка на 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:
Но когда у нас возникнет ошибка валидация входящих данных, мы обнаружим, что…
Скрин выше — это то, как будет отображена ошибка валидации. Как мы видим, она до сих пор в формате 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 тоже:
После всех изменений наше приложение теперь не только отдает и принимает JSON данные в формате Snake Case, но и показывает валидационные ошибки тоже в том виде, в котором нам нужно. Здесь по ссылке вы можете открыть Github репозиторий, где есть пример настроенного приложения. По описанным шагам вы можете применить не только Snake Case, но и любой другой формат сериализации данных, который вам по душе.
ссылка на оригинал статьи https://habr.com/ru/post/543370/
Добавить комментарий