使用Scaffolding功能建立Web API - 2
.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:N240826401
出刊日期: 2024/8/7
本篇文章將延續本站《使用Scaffolding功能建立Web API - 1》一文的情境,介紹如何使用Visual Studio 2022的Scaffolding功能建立Web API來開發ASP.NET Core Web API,利用工具的圖型介面進行資料移轉以及測試Web API。
管理連結服務
Visual Studio 2022現在內建管理連結服務的功能,從「Solution Explorer」視窗,專案名稱下的「Connected Services」項目上方按滑鼠右鍵,從快捷選單選擇「Manage Connected Services」項目,請參考下圖所示:
圖 1:「Manage Connected Services」項目。
使用資料移轉功能
接下來會開啟一個畫面,讓你選擇要連線的服務,點選「SQL Server Express LocalDb (Local)」右方的「...」按鈕,從快捷選單選擇「Add migration」項目,請參考下圖所示:
圖 2:「Add migration」項目。
接下來會看到「Entity Framework Migrations」視窗,要求取一個名稱,例如本例中的「Initial」,並選取「MyAPIDemoContext」類別,然後按下「Finish」按鈕,請參考下圖所示:
圖 3:「Entity Framework Migrations」視窗。
接下來按下「Entity Framework Migrations」視窗「Finish」按鈕,關閉「Entity Framework Migrations」視窗,請參考下圖所示:
圖 4:關閉「Entity Framework Migrations」視窗。
接著Visual Studio 2022會在專案中「Migrations」資料夾內,產生一個「xxx_ Initial.cs」檔案,描述即將要對資料庫結構的異動,請參考以下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace MyAPIDemo.Migrations { /// <inheritdoc /> public partial class Initial : Migration { /// <inheritdoc /> protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Book" , columns: table => new { Id = table.Column< int >(type: "int" , nullable: false ) .Annotation( "SqlServer:Identity" , "1, 1" ), Title = table.Column< string >(type: "nvarchar(50)" , maxLength: 50, nullable: false ), Price = table.Column< int >(type: "int" , nullable: false ), PublishDate = table.Column<DateTime>(type: "datetime2" , nullable: false ), InStock = table.Column< bool >(type: "bit" , nullable: false ), Description = table.Column< string >(type: "nvarchar(50)" , maxLength: 50, nullable: true ) }, constraints: table => { table.PrimaryKey( "PK_Book" , x => x.Id); }); } /// <inheritdoc /> protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Book" ); } } } |
此外專案根目錄下「MyAPIDemoContextModelSnapshot.cs」檔案內則包含了模型快照(Model Snapshot)程式碼,它記錄了資料庫模型的結構和配置。當你進行資料庫移轉時,Entity Framework Core 會根據這個模型快照來生成相應的 SQL 陳述句,以使資料庫的結構與應用程式的模型保持同步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | // <auto-generated /> using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using MyAPIDemo.Data; #nullable disable namespace MyAPIDemo.Migrations { [DbContext( typeof (MyAPIDemoContext))] partial class MyAPIDemoContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation( "ProductVersion" , "8.0.4" ) .HasAnnotation( "Relational:MaxIdentifierLength" , 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); modelBuilder.Entity( "MyAPIDemo.Book" , b => { b.Property< int >( "Id" ) .ValueGeneratedOnAdd() .HasColumnType( "int" ); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property< int >( "Id" )); b.Property< string >( "Description" ) .HasMaxLength(50) .HasColumnType( "nvarchar(50)" ); b.Property< bool >( "InStock" ) .HasColumnType( "bit" ); b.Property< int >( "Price" ) .HasColumnType( "int" ); b.Property<DateTime>( "PublishDate" ) .HasColumnType( "datetime2" ); b.Property< string >( "Title" ) .IsRequired() .HasMaxLength(50) .HasColumnType( "nvarchar(50)" ); b.HasKey( "Id" ); b.ToTable( "Book" ); }); #pragma warning restore 612, 618 } } } |
專案中的「XXX_Initial.Designer.cs」檔案也是工具自動生成的,給開發工具使用,參考以下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | // <auto-generated /> using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using MyAPIDemo.Data; #nullable disable namespace MyAPIDemo.Migrations { [DbContext( typeof (MyAPIDemoContext))] [Migration( "20240621074423_Initial" )] partial class Initial { /// <inheritdoc /> protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation( "ProductVersion" , "8.0.4" ) .HasAnnotation( "Relational:MaxIdentifierLength" , 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); modelBuilder.Entity( "MyAPIDemo.Book" , b => { b.Property< int >( "Id" ) .ValueGeneratedOnAdd() .HasColumnType( "int" ); SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property< int >( "Id" )); b.Property< string >( "Description" ) .HasMaxLength(50) .HasColumnType( "nvarchar(50)" ); b.Property< bool >( "InStock" ) .HasColumnType( "bit" ); b.Property< int >( "Price" ) .HasColumnType( "int" ); b.Property<DateTime>( "PublishDate" ) .HasColumnType( "datetime2" ); b.Property< string >( "Title" ) .IsRequired() .HasMaxLength(50) .HasColumnType( "nvarchar(50)" ); b.HasKey( "Id" ); b.ToTable( "Book" ); }); #pragma warning restore 612, 618 } } } |
更新資料庫
回到「Manage Connected Services」畫面,點選「SQL Server Express LocalDb (Local)」右方的「...」按鈕,從快捷選單選擇「Update database」項目以更新資料庫,請參考下圖所示:
圖 5: 更新資料庫。
下一步在「Entity Framework Migrations」視窗「DbContext class names」項目選取「MyAPIDemoContext」類別,然後按下「Finish」按鈕,請參考下圖所示:
圖 6:選取「MyAPIDemoContext」類別。
接下來按下「Entity Framework Migrations」視窗「Finish」按鈕,關閉「Entity Framework Migrations」視窗,請參考下圖所示:
圖 7:關閉「Entity Framework Migrations」視窗。
使用Swagger測試Web API
除了利用「MyAPIDemo.http」測試Web API的功能之外,預設專案也支援Swagger,只要執行網站,就會自動啟動瀏覽器,開啟Swagger UI讓你測試Web API,請參考下圖所示:
圖 8:使用Swagger測試Web API。
測試Web API功能
以下分別說明如何利用Swagger來測試Web API功能:
Post
使用Swagger送出HTTP Post請求,提供以下JSON格式的資料:
1 2 3 4 5 6 7 8 | { "id" : 0, "title" : "Programming" , "price" : 123, "publishDate" : "2024-06-21" , "inStock" : true , "description" : "Programming book" } |
執行結果請參考下圖所示:
圖 9:測試Post功能。
參考下圖是送出HTTP Post請求新增一筆「Book」資料的執行結果:
圖 10:新增一筆「Book」資料。
Get
參考下圖是使用Swagger測試頁面送出HTTP Get請求取回所有圖書資料的執行結果:
圖 11:取回所有圖書資料。
參考下圖是送出HTTP Get請求取回「id」為「1」的「Book」資料之執行結果:
圖 12:取回「id」為「1」的「Book」資料。
Put
送出HTTP Put請求時,我們提供以下JSON格式的資料來修改「id」為「1」的「Book」資料:
1 2 3 4 5 6 7 8 | { "id" : 1, "title" : "Programming Web API" , "price" : 1230, "publishDate" : "2024-06-21" , "inStock" : true , "description" : "Programming Web API book" } |
執行結果請參考下圖所示:
圖 13:送出HTTP Put請求。
當送出HTTP Put請求時,會得到一個500號例外錯誤,錯誤訊息如下:
Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot update identity column 'Id'.
at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
例外錯誤的資訊,請參考下圖所示:
圖 14:HTTP Put產生500號例外錯誤。
從錯誤訊息中得知,當進行修改動作時,不允許修改「id」,讓我們修改工具產生的「BookEndpoints.cs」程式碼,將「SetProperty(m => m.Id, book.Id)」這行程式刪除:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | group.MapPut( "/{id}" , async Task<Results<Ok, NotFound>> ( int id, Book book, MyAPIDemoContext db) => { var affected = await db.Book .Where(model => model.Id == id) .ExecuteUpdateAsync(setters => setters .SetProperty(m => m.Id, book.Id) .SetProperty(m => m.Title, book.Title) .SetProperty(m => m.Price, book.Price) .SetProperty(m => m.PublishDate, book.PublishDate) .SetProperty(m => m.InStock, book.InStock) .SetProperty(m => m.Description, book.Description) ); return affected == 1 ? TypedResults.Ok() : TypedResults.NotFound(); }) .WithName( "UpdateBook" ) .WithOpenApi(); |
目前「MapPut」方法的程式碼看起來如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | group.MapPut( "/{id}" , async Task<Results<Ok, NotFound>> ( int id, Book book, MyAPIDemoContext db) => { var affected = await db.Book .Where(model => model.Id == id) .ExecuteUpdateAsync(setters => setters .SetProperty(m => m.Title, book.Title) .SetProperty(m => m.Price, book.Price) .SetProperty(m => m.PublishDate, book.PublishDate) .SetProperty(m => m.InStock, book.InStock) .SetProperty(m => m.Description, book.Description) ); return affected == 1 ? TypedResults.Ok() : TypedResults.NotFound(); }) .WithName( "UpdateBook" ) .WithOpenApi(); |
完成之後,再使用Swagger測試,這次可以正確修改圖書資料,參考下圖是送出HTTP Put請求修改圖書資料的執行結果:
圖 15::Put請求執行結果。
Delete
參考下圖是送出HTTP Delete請求刪除「Book」資料的執行結果:
圖 16:送出HTTP Delete請求刪除圖書資料。
使用Endpoint explorer瀏覽與測試Web API
Visual Studio 2022還有一個小功能,能讓你很容易找尋、測試Web API端點。只要選取「View」>「Endpoints Explorer」開啟視窗,請參考下圖所示:
圖 17:開啟「Endpoints Explorer」視窗。
「Endpoints Explorer」視窗會自動偵測並列出中專案中的Web API端點,請參考下圖所示:
圖 18:自動偵測並列出中專案中的Web API端點。
你可以點選「Endpoints Explorer」視窗其中的請求項目,從快捷選單選擇「Generate Request」項目,就會自動開啟「*.http」檔案讓你進行測試,請參考下圖所示:
圖 19:「Generate Request」項目。
在開啟的「*.http」檔案中,會自動生成送出請求的指令,請參考以下程式碼:
1 2 3 4 5 6 7 8 9 10 | @MyAPIDemo_HostAddress = https: //localhost:7005 GET {{MyAPIDemo_HostAddress}}/weatherforecast/ Accept: application/json ### GET {{MyAPIDemo_HostAddress}}/api/Book/ ### |
接著便可以點選指令上方的「Send Request」連結進行測試,請參考下圖所示:
圖 20:測試Web API。
0 意見:
張貼留言