ASP.NET Web API の Core 3.1 版のアプリが .NET Framework 版のアプリと異なる点を備忘録としてまとめておきます。あくまで自分的に大きな違いがあると思っている点のみです。
-
Controller は ControllerBase クラスを継承する。(.NET Framework の ApiController ではなく)
-
Controller に ApiControllerAttribute 属性を付与する。
-
ルーティングは Controller に RouteAttibute 属性を付与して設定する。(.NET Framework 版プロジェクトで生成される "api/{controller}/{id}" というようなルーティング設定はないようです)
-
アクションメソッドに [HttpGet], [HttpGet("{id}")], [HttpPost] 等の属性を付与する。(付与しないと HTTP 動詞でどれを呼ぶかの判別ができないようです)
-
アクションメソッドの引数、例えば [FromBody] string name の name に文字列をバインドする場合、.NET Framework 版では「=文字列」という文字列をコンテンツとして送信すれば可能でしたが、Core 版ではそれができない。
-
.NET のクラスのプロパティ名の最初の文字は大文字にするのが普通ですが、大文字にしておいてもシリアライズされた結果の応答の JSON 文字列のキー名の最初の文字が小文字になる。
詳しくは Microsoft のドキュメント「ASP.NET Core を使って Web API を作成する」に書いてありますのでそちらを見てください。
コード例を示すと以下のようになります。先の記事「ASP.NET Web API のバインディング」に .NET Framework 版のコードがありますので、それと比較してみてください。
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using WebAPI.Models;
namespace WebAPI.Controllers
{
[Route("[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private List<Hero> heroes = new List<Hero> {
new Hero {Id = 1, Name = "スーパーマン"},
new Hero {Id = 2, Name = "バットマン"},
new Hero {Id = 3, Name = "ウェブマトリクスマン"},
new Hero {Id = 4, Name = "チャッカマン"},
new Hero {Id = 5, Name = "スライムマン"}
};
// GET api/values (Read...すべてのレコードを取得)
[HttpGet]
public List<Hero> Get()
{
return heroes;
}
// GET api/values/5 (Read...id 指定のレコード取得)
[HttpGet("{id}")]
public Hero Get(int id)
{
return heroes[id - 1];
}
// POST api/values (Create...レコード追加)
[HttpPost]
public List<Hero> Post(Hero postedHero)
{
heroes.Add(postedHero);
return heroes;
}
// PUT api/values/5 (Update...id 指定のレコード更新)
[HttpPut("{id}")]
public List<Hero> Put(int id, Hero postedHero)
{
heroes[id - 1].Name = postedHero.Name;
return heroes;
}
// DELETE api/values/5 (Delete...id 指定のレコード削除)
[HttpDelete("{id}")]
public List<Hero> Delete(int id)
{
heroes.RemoveAt(id - 1);
return heroes;
}
}
}
上に列挙した 5 番目の項目に書いた件を説明します。上のコード例の Put の場合を見てください。.NET Framework 版の記事ではアクションメソッドの引数を Put(int id, [FromBody] string name) とし、それを呼び出す jQuery ajax のコードでは data: encodeURI("=ガッチャマン") として無理やり(?)引数 name にバインドできていました。Core 版ではそれはできないようで、以下のコードのようにしないとダメでした。
// .NET Framework ベースとは異なり、以下のようにしないとダメ
function apiHeroesPut5() {
var j = { Id: 5, Name: "ガッチャマン" };
var jsonString = JSON.stringify(j);
$.ajax({
type: "PUT",
url: "/values/5",
data: jsonString,
contentType: "application/json; charset=utf-8",
success: function (data) {
$('#heroes').empty();
$.each(data, function (key, val) {
var str = val.id + ': ' + val.name;
$('<li/>', { html: str }).appendTo($('#heroes'));
});
},
error: function (jqXHR, textStatus, errorThrown) {
$('#heroes').empty();
$('#heroes').text('textStatus: ' + textStatus +
', errorThrown: ' + errorThrown);
}
});
}
理由は不明です。バインディング ソース パラメーター推論を読むと分かるかも。手抜きですみません。
最後に、上に列挙した 6 番目の項目に書いた、シリアライズされた結果の応答の JSON 文字列のキー名の最初の文字が小文字になる件を説明します。
上の 1 番目のコード例を見てください。Hero クラスのプロパティ名 Id, Name の最初の文字は大文字にしています。しかし、Web API が Hero オブジェクトを JSON 文字列にシリアライズすると何故かキー名は id, name というように小文字になります。
これは Camel Casing と言うそうです。Core 3.0 以降の MVC でも同様で、MVC や Web API のフレームワークでデフォルトでそのようにする設定がされているようです(コンソールアプリで試したときは Camel Casing にはなりませんでしたので、JsonSerializer クラス自体の設定ではなさそうです)。Camel Casing になるのを回避する方法はあります。詳しくは別の記事「JsonSerializer の Camel Casing」を見てください。
2 番目のコード例の success: function (data) の data には応答の JSON 文字列をデシリアライズした JavaScript オブジェクトが渡されますが、キー名から値を取得する際、最初の文字を小文字にしないと値を取得できず undefined になってしまいます。(なので上のコードでは val.id, val.name としています)
何を隠そう自分はここにハマって 1 ~ 2 時間悩みました。(笑)