У меня есть два контроллера контроллера: ControllerA
и ControllerB
. Базовый класс каждого контроллера - Controller
.
ControllerA
должен возвращать JSON в формате по умолчанию (camelCase). ControllerB
должен возвращать данные в другом формате JSON: snake_case.
Как я могу реализовать это в ASP.NET Core 3.x и 2.1?
Я пробовал startup
с:
services
.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new StringEnumConverter());
options.SerializerSettings.ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new SnakeCaseNamingStrategy()
};
})
.AddControllersAsServices();
Но это изменит сериализацию для контроллеров все, а не только для ControllerB
. Как я могу настроить или аннотировать эту функцию для 1 контроллера?
Вы можете добиться этого с помощью комбинации Фильтр действий и Форматирование вывода.
В версии 3.0+ все выглядит немного иначе, где средства форматирования JSON по умолчанию для версии 3.0+ основаны на System.Text.Json
. На момент написания эти не имеют встроенной поддержки стратегии именования змейки.
Однако, если вы используете Json.NET с 3.0+ (подробности в документы), SnakeCaseAttribute
из приведенного выше по-прежнему жизнеспособен с парой изменений:
JsonOutputFormatter
теперь NewtonsoftJsonOutputFormatter
.NewtonsoftJsonOutputFormatter
требуется аргумент MvcOptions
.Вот код:
public class SnakeCaseAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext ctx)
{
if (ctx.Result is ObjectResult objectResult)
{
objectResult.Formatters.Add(new NewtonsoftJsonOutputFormatter(
new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
},
ctx.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
ctx.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
}
}
}
Вы можете добиться этого с помощью комбинации Фильтр действий и Форматирование вывода. Вот пример того, как может выглядеть фильтр действий:
public class SnakeCaseAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext ctx)
{
if (ctx.Result is ObjectResult objectResult)
{
objectResult.Formatters.Add(new JsonOutputFormatter(
new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
},
ctx.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>()));
}
}
}
При использовании OnActionExecuted
код запускается после соответствующего действия и сначала проверяет, является ли результат ObjectResult
(что также относится к OkObjectResult
благодаря наследованию). Если это ObjectResult
, фильтр просто добавляет настроенную версию JsonOutputFormatter
, которая будет сериализовать свойства с помощью SnakeCaseNamingStrategy
. Второй параметр в конструкторе JsonOutputFormatter
извлекается из контейнера DI.
Чтобы использовать этот фильтр, просто примените его к соответствующему контроллеру:
[SnakeCase]
public class ControllerB : Controller { }
Примечание: вы можете захотеть создать JsonOutputFormatter
/ NewtonsoftJsonOutputFormatter
заранее где-нибудь, например - я не заходил так далеко в примере, поскольку это вторично по отношению к рассматриваемому вопросу.
Закончил создание этого метода, который я использую на своих конечных точках:
{
// needed to get the same date and property formatting
// as the Search Service:
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver()
{
NamingStrategy = new SnakeCaseNamingStrategy()
},
DateFormatString = "yyyy-MM-ddTHH:mm:ss.fffZ"
};
return Json(result, settings);
}
Нет необходимости в фильтрах действий и т. д. Просто переопределите Json()
в вашем контроллере и все.
public class MyController : Controller
{
public override JsonResult Json(object data)
{
return base.Json(data, new JsonSerializerSettings {
// set whataever default options you want
});
}
}