更改 Api 回應的參數命名規則(PascalCase 及 CamelCase)
在專案上有建立了一個 Middleware,把 exception 轉為自訂的 response 格式回應出去,結果發現從 action 出來的是 CamelCase,middleware 處理完的是 PascalCase,導致前端會依照情況不同拿到 Error 跟 error,為了讓兩邊統一,筆記一下做法
讓 System.Text.Json 的回應為 PascalCase
.NetCore 3.X的Json函式庫為System.Text.Json,而API回應格式預設為小寫開頭的駝峰式命名(camelCase),以WebApi的範例為例
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
從 PostMan 發出請求拿到的回應
若要讓回應格式改為 PascalCase,需要在 Startup 加上
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
}
接著Run起來再發一次請求,這次的回應就沒有被改成 CamelCase 了
讓 Json.Net 的回應返回 PascalCase
雖然在 .NetCore 3.X 了,但是有些情況還是希望可以使用 Json.Net,所以接著把 Json.Net 裝上去
dotnet add package Newtonsoft.Json
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
然後在 Startup 加入 Json.Net 的服務
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
}
再次呼叫 API,完美的又拿到了 CamelCase 的回應
因為已經改用 Json.Net 來做轉序列化的動作了,所以需要調整 AddNewtonsoftJson 的 options
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
})
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
}
再次對API發出請求,預期的返回 PascalCase 的回應
讓 Json.Net Serialize CamelCase
當發現上述的問題的時候,前端已經使用 CamelCase 串接了不少的頁面,評估成本還是有後端統一丟 CamelCase 比較省工一點,下面是處理 Exception 的 Middleware
public class ExceptionMiddleware
{
private readonly ILoggerFactory _loggerFactory;
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_loggerFactory = loggerFactory;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next.Invoke(context);
}
catch (Exception ex)
{
var logger = _loggerFactory.CreateLogger("ExceptionHandler");
logger.LogError(ex, ex.Message);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
Code = HttpStatusCode.InternalServerError,
Error = "Server Error",
}));
}
}
}
先把原本在 Startup 上面加上的設定先拿掉,讓API回應為 CamelCase
稍微修改讓他拋出一個 Exception
這邊就會看到了兩種回應兩種的命名 case,接著只需要調整 Middleware,改用 SerializeObject 的另一個多載
var response = new
{
Code = HttpStatusCode.InternalServerError,
Error = "Server Error",
};
var settings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, settings));
這樣在 Middleware 處理後的回應也是 CamelCase 了
在專案上有建立了一個 Middleware,把 exception 轉為自訂的 response 格式回應出去,結果發現從 action 出來的是 CamelCase,middleware 處理完的是 PascalCase,導致前端會依照情況不同拿到 Error 跟 error,為了讓兩邊統一,筆記一下做法
讓 System.Text.Json 的回應為 PascalCase
.NetCore 3.X的Json函式庫為System.Text.Json,而API回應格式預設為小寫開頭的駝峰式命名(camelCase),以WebApi的範例為例
[HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); }
public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } }
從 PostMan 發出請求拿到的回應
若要讓回應格式改為 PascalCase,需要在 Startup 加上
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; }); }
接著Run起來再發一次請求,這次的回應就沒有被改成 CamelCase 了
讓 Json.Net 的回應返回 PascalCase
雖然在 .NetCore 3.X 了,但是有些情況還是希望可以使用 Json.Net,所以接著把 Json.Net 裝上去
然後在 Startup 加入 Json.Net 的服務
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddNewtonsoftJson() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; }); }
再次呼叫 API,完美的又拿到了 CamelCase 的回應
因為已經改用 Json.Net 來做轉序列化的動作了,所以需要調整 AddNewtonsoftJson 的 options
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver(); }) .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; }); }
再次對API發出請求,預期的返回 PascalCase 的回應
讓 Json.Net Serialize CamelCase
當發現上述的問題的時候,前端已經使用 CamelCase 串接了不少的頁面,評估成本還是有後端統一丟 CamelCase 比較省工一點,下面是處理 Exception 的 Middleware
public class ExceptionMiddleware { private readonly ILoggerFactory _loggerFactory; private readonly RequestDelegate _next; public ExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) { _next = next; _loggerFactory = loggerFactory; } public async Task InvokeAsync(HttpContext context) { try { await _next.Invoke(context); } catch (Exception ex) { var logger = _loggerFactory.CreateLogger("ExceptionHandler"); logger.LogError(ex, ex.Message); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Code = HttpStatusCode.InternalServerError, Error = "Server Error", })); } } }
先把原本在 Startup 上面加上的設定先拿掉,讓API回應為 CamelCase
稍微修改讓他拋出一個 Exception
這邊就會看到了兩種回應兩種的命名 case,接著只需要調整 Middleware,改用 SerializeObject 的另一個多載
var response = new { Code = HttpStatusCode.InternalServerError, Error = "Server Error", }; var settings = new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() }; await context.Response.WriteAsync(JsonConvert.SerializeObject(response, settings));
這樣在 Middleware 處理後的回應也是 CamelCase 了