🎭
[ASP.NET Core WebAPI] application/x-www-form-urlencoded のモデルバインドを制御する
application/x-www-form-urlencoded
におけるモデルバインドで、リクエストがsnake_caseなどになっていてプロパティと別名でもバインドする方法です。巷に全然情報が無いです。
通常の例
[FromForm]
を付けることで、application/x-www-form-urlencoded
で来るリクエストをモデルにバインドできます。
public record MyRequest
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
[ApiController]
public class MyController : ControllerBase
{
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public IActionResult Post([FromForm] MyRequest request)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok($"FirstName={request.FirstName}, LastName={request.LastName}");
}
}
これで例えば FirstName=Donald&LastName=Trump
のようなリクエストを送れば、 MyRequest
モデルにその値が入った状態でアクションメソッドが呼ばれます。
snake_caseの例
camelCaseからPascalCaseであれば名前解決してくれるようですが、snake_caseだとうまくいきません。
もちろん以下のようにするのは一つの解ですが、C#的には気持ち悪いですね。
public record MyRequest
{
public string first_name { get; init; }
public string last_name { get; init; }
}
そこでこのようにすれば自由に設定できます。
public record MyRequest
{
[FromForm(Name = "first_name")]
public string FirstName { get; init; }
[FromForm(Name = "last_name")]
public string LastName { get; init; }
}
これで、first_name=Donald&last_name=Trump
のようなリクエストでもちゃんとバインドできます。
それにしても
[FromForm]
ってややこしいですね。fromとformで間違い探しみたいで・・・
JSONとモデルを共用する
application/x-www-form-urlencoded
でも application/json
でも受け付けられるというWebAPIにしたい際、モデル定義は共用できます。
使うJSONシリアライザによりますが、例えば以下のようにできます。
[DataContract]
public record MyRequest
{
[DataMember(Name = "first_name")]
[FromForm(Name = "first_name")]
public string FirstName { get; init; }
[DataMember(Name = "last_name")]
[FromForm(Name = "last_name")]
public string LastName { get; init; }
}
[ApiController]
public class MyController : ControllerBase
{
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public IActionResult PostUrlEncoded([FromForm] MyRequest request)
{
return Ok("UrlEncoded");
}
[HttpPost]
[Consumes("application/json")]
public IActionResult PostJson([FromBody] MyRequest request)
{
return Ok("JSON");
}
}
Discussion