Asp.net Core以前は、すべてのActionの戻り値はActionResultであり、Json()、File()およびその他のメソッドはActionResultサブクラスを返していました。CoreがMVCとWebApiをマージした後、アクションの戻り値システムも大幅に変更されました。
ActionResultクラス
ActionResultクラスは、最も一般的に使用される戻り値の型です。基本的には前のAsp.net MVCセットに従いますが、ほとんどの場合それを使用しても問題ありません。たとえば、表示に戻る、jsonに戻る、ファイルに戻るなどに使用します。非同期の場合は、タスクを使用します
public class TestController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult MyFile()
{
return File(new byte[] { }, "image/jpg");
}
public ActionResult MyJson()
{
return Json(new { name = "json" });
}
public ActionResult Ok()
{
return Ok();
}
}
IActionResultインターフェース
ActionResultクラスはIActionResultインターフェイスを実装するため、ActionResultの代わりにIActionResultを使用できます。それも非同期の場合は、戻り値としてタスクパッケージを使用します。
public class ITestController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult MyFile()
{
return File(new byte[] { }, "image/jpg");
}
public IActionResult MyJson()
{
return Json(new { name = "json" });
}
public IActionResult HttpOk()
{
return Ok();
}
public async Task<IActionResult> AsyncCall()
{
await Task.Delay(1000);
return Content("ok");
}
}
POCOに直接戻る
Asp.net Coreのコントローラーアクションは、POCOタイプ(実際には必ずしもPOCOクラスではなく、どのタイプでもかまいませんが、通常はviwemodelなどのPOCOクラスを使用すると返されます)をActionResultまたはIActionResultとは限りません。Asp.net Coreフレームワークは、自動的にシリアル化してフロントエンドに戻るのに役立ちます。デフォルトでは、jsonシリアル化が使用されます。それも非同期の場合は、戻り値としてタスクパッケージを使用します。
public class Person
{
public string Name { get; set; }
public string Sex { get; set; }
}
public class ITestController : Controller
{
public Person GetPerson()
{
return new Person { Name = "abc", Sex = "f" };
}
public async Task<List<Person>> GetPersons()
{
await Task.Delay(1000);
return new List<Person> {
new Person { Name = "abc", Sex = "f" },
new Person { Name = "efg", Sex = "m" }
};
}
}
ActionResult <T>ジェネリッククラス
安静なwebapiシステムを設計するとき、POCOを戻り値として使用することに慣れています。たとえば、Personを取得するAPIを設計します。001から/ person / 001のURLを取得します。
[Route("[controller]")]
public class PersonController : Controller
{
IPersonRepository _repository;
PersonController(IPersonRepository repository)
{
_repository = repository;
}
[HttpGet("{id}")]
public Person Get(string id)
{
return _repository.Get(id);
}
}
この方法は問題ないようですが、実際には小さな問題があります。repository.GetメソッドがIDに基づいてデータを見つけられない場合、nullを返します。Actionの戻り値としてnullが使用されている場合、最終フレームは204 httpステータスコードに変換されます。
204はコンテンツなしを意味します。安らかなAPIとして、204のセマンティクスはここで問題になります。ここでより適切なステータスコードは404 NOT FOUNDです。それを変更しましょう:
[HttpGet("{id}")]
public Person Get(string id)
{
var person = _repository.Get(id);
if (person == null)
{
Response.StatusCode = 404;
}
return person;
}
個人データが見つからない場合、システムは404 Not Foundを返します。
しかし、ControllerBaseにはNotFoundResult NotFound()メソッドが組み込まれているため、これは明らかに十分にエレガントではありません。この方法を使用すると、コードがますます明確になります。変更を続けます:
[HttpGet("{id}")]
public Person Get(string id)
{
var person = _repository.Get(id);
if (person == null)
{
return NotFound();
}
return person;
}
残念ながら、このコードVSはエラーを表示します。戻り値の型に一貫性がないため。メソッドシグネチャの戻り値はPersonですが、メソッド内でNotFoundResultが返され、Personが1回返されます。
この問題を解決するには、ActionResult <T>が表示されます。私たちは変化し続けます:
[HttpGet("{id}")]
public ActionResult<Person> Get(string id)
{
var person = _repository.Get(id);
if (person == null)
{
return NotFound();
}
return person;
}
これでVSはエラーを報告せず、実行後に正常に動作します。しかし、それについて考えるのも非常に奇妙です、なぜ戻り値の型がActionResult <Person>に変更され、それが間違っていないのですか?明らかに、戻り値の型はメソッドのシグネチャと一致していませんか?
詳細なActionResult <T>
上記の質問に続いて、ActionResultの内部を見てみましょう。
ここを参照して、元のActionResult <T>に2つの暗黙の演算子メソッドが組み込まれていることを理解してください。暗黙の演算子は、暗黙の型変換を宣言するために使用されます。
public static implicit operator ActionResult<TValue>(ActionResult result);
ActionResultタイプをActionResult <TValue>タイプに変換できることを示します。
public static implicit operator ActionResult<TValue>(TValue value)
TValue型をActionResult <TValue>型に変換できることを示します。
これら2つの方法があるため、ActionResultまたはTValue型がActionResult <T>に割り当てられると、自動型変換が実行されます。したがって、VSはここでエラーを報告しません。
まとめ
- ほとんどの場合、Actionの戻り値はActionResult / IActionResultを使用できます
- RESTful APIを設計する場合、POCOクラスを戻り値として直接使用できます
- POCOクラスまたはActionResultクラスの両方の戻り値を戻り値としてサポートするアクションを設計する場合は、戻り値としてActionResult <T>を使用できます。
- ActionResult <T>が2種類の戻り値の型をサポートできる理由は、暗黙の演算子に2つの暗黙の変換メソッドが組み込まれているためです。