.NET 6入門
1、ApiControllerAttribute
- 実際には
[ApiController]はApiControllerAttributeクラスであり、角括弧の外側に完全名を記述する必要があり、角括弧内では省略形でよい
2、ApiControllerによるデータソース推論
(0)パラメータアトリビュート(非推奨)
- .NET Core 3.1以前では、メソッド内で渡されるパラメータが不明な場合、以下のアトリビュートを使用してパラメータを修飾する必要がある。さもなければエラーとなる。
- .NET Core 3.1以降では、コントローラーに
[ApiController]が存在しない場合にのみ、以下の修飾子を使用する。 ApiControllerの導入により、オブジェクトの自動推論が可能になった。
アトリビュートバインドソース[FromBody]このパラメータはリクエストボディから取得される[FromForm]リクエストボディ内のフォームデータ[FromHeader]リクエストヘッダー[FromQuery]リクエストクエリ文字列パラメータ[FromRoute]現在のリクエストにおけるルートパラメータ[FromServices]操作パラメータとして挿入されたサービスリクエスト
namespace SecondDemo.Controllers
{
//[ApiController]
[Route("/api/[controller]/[action]")]
public class TestPostController : ControllerBase
{
//モデルオブジェクトを使用してフロントエンドからのデータを受け取る
[HttpPost]
public string Post([FromBody] TestPostViewModel? model)
{
return model.I + model.Name;
}
}
}
-
[ApiController]属性はコントローラークラスに適用することで、API固有の動作を有効にする -
ルート属性の必須条件
-
自動HTTP400応答
-
バインドソースパラメータの推論
-
Multipart / form-dataリクエストの推論、複数ファイル・データアップロード、自動リクエスト推論 -
エラー状態コードに関する詳細情報
-
データ検証、ルールに適合しない場合はエラーを発生させる
namespace SecondDemo.Models
{
public class TestPostViewModel
{
[MaxLength(3)] //最大長3
[Required] //必須項目
public int I { get; set; }
public string? Name { get; set; }
}
}
(1)モデルを使用してフロントエンドからのJSONオブジェクトを受信
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const userName = ref("NiHao");
const onGetValue = () => {
axios.post("http://localhost:5203/api/HelloWorld/Post", {i:666, name:"asd"})
.then(res =>{
userName.value = res.data;
})
}
return {userName, onGetValue}
}
})
</script>
namespace SecondDemo.Models
{
public class TestPostViewModel
{
public int I { get; set; }
public string? Name { get; set; }
}
}
namespace SecondDemo.Controllers
{
[ApiController]
[Route("/api/[controller]/[action]")]
public class TestPostController : ControllerBase
{
//モデルオブジェクトを使用してフロントエンドからのデータを受け取る
[HttpPost]
public string Post(TestPostViewModel? model)
{
return model.I + model.Name;
}
}
}
3、RestfulスタイルAPIの作成
API説明リクエスト本文レスポンス本文GET /api/todoitemsすべてのTODOアイテムを取得なしTODOアイテムの配列GET /api/todoitems/{id}IDでアイテムを取得なしTODOアイテムPOST /api/todoitems新しいアイテムを追加TODOアイテム(エンティティ)TODOアイテム(エンティティのID)PUT /api/todoitems/{id}既存アイテムを更新TODOアイテム(エンティティ)なしDELETE /api/todoitems/{id}アイテムを削除なしなし
4、Webアプリケーションの完全なプロセス
5、三つの戻り値の型
ASP.NET Coreは以下のWebApiコントローラーオペレーションの戻り値型オプションを提供する
- 特定の型
- IActionResult
- ActionResult
(1)特定の型
- 最も簡単な操作はプリミティブ型または複雑なデータ型(例えば
sringやカスタムオブジェクト)を返す
[HttpGet]
public List<Produch> Get()
{
return _repostiory.GetProducts();
}
(2)IActionResult
- マイクロサービスプロジェクトでは、ステータスコード情報が少なく、帯域幅を節約し、プロジェクトの同時アクセスリソース消費が少ないため、ステータスコードを戻り値として使用し、インスタンスのハートビート監視を行う。
- 操作中に複数の
ActionResult戻り値型が考えられる場合にIActionResultを使用し、複数のHTTPステータスコードを表現する。このクラスの一般的な戻り値型にはBadRequestResult(400)、NotFound(404)、OkObjectResult(200)が含まれる。 Ok()の便利メソッドは、return new OkObjectResult(arg)の簡潔な記述であり、引数を一つ渡すことができるが、ドキュメントには戻り値型の例が示されていない。
namespace SecondDemo.Controllers
{
[ApiController]
[Route("/api/[controller]/[action]")]
public class TestIActionResultController : ControllerBase
{
//200を戻す引数なし
[HttpGet]
public IActionResult GetOkNoArgs()
{
//Ok()便利メソッドはreturn new OkObjectResult(arg)の省略記法
return Ok();
}
//200を戻す引数あり
[HttpGet]
public IActionResult GetOk()
{
return Ok(new TestPostViewModel { I = 123, Name = "asd" });
}
}
}
(3)ActionResult
-
ASP.NET coreはWeb Apiコントローラーオペレーション向けにActionResult型を含む -
この戻り値は通常の型の戻り値だけでなく、指定されたステータスコードも返すことができる。
//ActionResultを使用
[HttpGet("{id}")]
public ActionResult<Product> GetProductById(int id)
{
if (id != 1)
{
return NotFound();
}
return new Product { Id = 1, Name = "asda"};
}
//同様に、IActionResultでも同じタスクを実行できる
[HttpGet("{id}")]
public IActionResult GetProductById(int id)
{
if (id != 1)
{
return NotFound();
}
return Ok(new Product { Id = 1, Name = "dnisai" });
}
6、Minimal APIS
最小APIは、Program.csで宣言される
-
新しいホスティングAPI
-
WebApplicationとWebApplicationBuilder -
新しいルーティングAPI
-
MapXxx(コンテキストアドレス、エンドポイントで実行されるデリゲート)
-
URLとして理解できる
-
実行後に返す/応答するもの
-
デリゲートは直接宣言、ラムダ式、または静的メソッドとして指定可能
//サービス登録
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//Webアプリケーションの作成
var app = builder.Build();
//デリゲートの作成
//MapXxx(ルートアドレス、エンドポイントに到達したとき実行されるデリゲート
// () => statement...; return...;)
app.MapGet("/MinimalTest", () => "hello get");
app.MapPost("/MinimalTest", (Product p) => { return new TestPostViewModel { I = p.Id, Name = p.Name }; });
//静的メソッドをデリゲートパラメータとして使用
app.MapPut("/MinimalTest", Hello.HelloPut);
//ラムダ式で非匿名メソッドデリゲートを宣言
string HelloDelete() => "hello delete";
app.MapDelete("/MinimalTest", HelloDelete);
//Webアプリケーションの起動
app.Run();
public class Hello
{
public static string HelloPut()
{
return "hello";
}
}
(1)ルート制約
- ルートマッチングの動作を制限する
//ルート制約、ルートパラメータの制約
//idとしてint型のみ受け入れる
app.MapPost("MiniMalConsTest/{id:int}", (string id) => "パラメータ制約に合致 id:" + id);
//正規表現を使用し、a-z\0-9\_のみを受け入れる
app.MapPost("MiniMalConsTest/{password:regex(^[a-z0-9_-]+$)}", (string password) => "パラメータ制約に合致 pwd:" + password);
(2)パラメータ形式
- 一般的なリクエストパラメータ形式はRestfulと一致している
- MinimalAPISには依存性注入によるパラメータ形式もある
7、制御の反転、依存性注入
(1)IOC
-
ロジック層、データベース接続層は、使用時に私たちが要求するのではなく、それらがサービスを注入する
-
ASP.NET Core 6において、IOCコンテナは
ServiceCollectionサービスコレクターである -
サービスを格納するコンテナー
-
サービスは開発中に必要な各種クラスの総称
-
ServiceCollectionはWebプロジェクトでのみ使用できない
(2)DI注入
-
注入方法
-
コンストラクタ注入(基本)
-
プロパティ注入(便利、強化コンテナー)
-
強化コンテナー
AutoFacはプロパティ注入も可能で、AOP(横断的機能プログラミング)も行えるが、全体的なAOPを有効にするとパフォーマンスが低下するため、ネイティブAOPを使用する方が良い -
メソッド注入(ほとんど使われない)
-
依存性注入は制御の反転を実現する手段または方法である
-
依存するもの:アプリケーションはIOCコンテナに依存する
-
なぜ依存するか:アプリケーションはIOCコンテナが必要とする外部リソースを提供するために依存する
-
注入されるもの:IOCコンテナがアプリケーションの特定オブジェクトに注入する、アプリケーションが依存するオブジェクト
-
注入される内容:オブジェクト、リソース、定数データなど、オブジェクトが必要とする外部リソース
(3)WebApiでの使用例
1.サービスの作成
namespace SecondDemo.Services
{
public class UserService
{
public string GetUserName()
{
return "my username";
}
}
}
Program.csでサービスを登録し、UserServiceをIOCコンテナに配置
builder.Services.AddTransient<UserService>();
3.必要なControllerでプロパティ+コンストラクタ注入を行い、使用する
namespace SecondDemo.Controllers
{
[ApiController]
[Route("/api/[controller]/[action]")]
public class TestIOCController : ControllerBase
{
//プロパティの作成
private UserService UserService { get; }
//コンストラクタ注入でUserServiceを使用
public TestIOCController(UserService userService)
{
this.UserService = userService;
}
//IOCコンテナDI注入のテスト
[HttpGet]
public string GetUserName()
{
return UserService.GetUserName();
}
}
}
(5)Minimal Apisでの使用
//Minimal APISで依存性注入を使用
app.MapPost("MiniMalIOCDITest", (UserService userService) => userService.GetUserName())
(6)依存性逆転
- インターフェース規格でServiceを使用する場合、
IServiceとServiceの両方を登録する必要がある。 - 以下の二つのジェネリックメソッドを使用して
IServiceを登録し、第一ジェネリックはインターフェース、第二ジェネリックは実装クラスである。
//サービスのトランジェント登録、インターフェース型、実装型 IUserService、UserServiceをIOCコンテナに配置
builder.Services.AddTransient<IUserService, UserService>();
builder.Services.AddTransient<UserService>();
(7)IOCライフサイクル
-
Transientトランジェントライフサイクル -
そのプロパティを使用するたびに新しいインスタンスを作成する
-
Singletonシングルトンライフサイクル -
そのプロパティを使用する際に一度だけ新しいインスタンスを作成し、以降はそれを使用する
-
Scopedスコープライフサイクル スレッド単一 -
毎回のリクエストごとに新しいインスタンスを作成し、そのリクエスト内でそれを使用する
1.使用場面
-
Scoped異なるスレッドで複数回同じプロパティを共有する -
Transient同じプロパティを共有する必要がない -
Singleton登録されたプロパティは常に存在し、プロパティをキャッシュするようなもの -
builder.Services.AddSingleton<IService>(new Service(1));
##### 2.サービスの登録
- トランジェントの登録
builder.Services.AddTransient<サービス層インターフェースクラス, サービス層インターフェース実装クラス>(); builder.Services.AddTransient(typeof(サービス層インターフェースクラス), typeof(サービス層インターフェース実装クラス));
- スコープの登録
builder.Services.AddScoped<サービス層インターフェースクラス, サービス層インターフェース実装クラス>(); builder.Services.AddScoped<サービス層インターフェースクラス, サービス層インターフェース実装クラス>(typeof(IService), typeof(IService));
- シングルトンの登録
builder.Services.AddSingleton<サービス層インターフェースクラス, サービス層インターフェース実装クラス>(); builder.Services.AddSingleton(typeof(サービス層インターフェースクラス), typeof(サービス層インターフェース実装クラス)); //特徴、引数を渡す builder.Services.AddSingleton<サービス層インターフェースクラス>(service);