The standard way to display data in ASP.NET Web API is Camel Case. But sometimes tasks arise when you need to change the data format to something else. For example, on the frontend, you might have a SPA that works with data in the snake case format. In this article, I'll show you how to change the serialization format in ASP.NET Core Web API.
This article provides code examples that you will need to transfer to your project. At the end of the post there is a link to the Github repository, where I have already configured the application to serialize in a snake case. All code samples and the project in the repository are written in ASP.NET Core .net5 version.
Changing the serialization format of server requests and responses
All we need to do to change serialization is set the Naming Policy in the application settings. The standard policy is Camel Case. Installing a policy on a Snake Case is a simple task.
First, let's add utilitarian methods for transforming strings to the 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 :
After all the changes, our application now not only sends and receives JSON data in the Snake Case format, but also shows validation errors in the form in which we need. Here you can open the Github repository from the link where there is an example of a configured application. Through the steps described, you can apply not only Snake Case, but also any other data serialization format that you like.