Latest Posts

2024年4月10日 星期三

注意!保持PMP、ACP認證需要每3年60學分?快看如何更新


QPMP認證與ACP認證需要多久更新一次? 

PMP認證與ACP認證3年需要更新一次。3年內PMP認證需累積60PDU學分,ACP認證需累積30PDU學分,就可以更新;若3年沒有更新,有1年緩衝期,在緩衝期間達到累積學分即可更新認證,若超過緩衝期則要重考該認證。

QPMP、ACP認證有效日期該怎麼計算?

觀察2

若您100.1.1考上PMP認證(以證書日期為主),103.1.1前需要更新PMP認證,若無,103.1.1~104.1.1則為您的緩衝期,緩衝期間您的認證是無效的,PMI官網查詢到您的證書狀態為Suspend(暫停),若您在這一年內更新完成,您的第二張PMP證書的日期則為103.1.1~106.1.1;換言之,緩衝期的一年是不會影響您的證書起訖時間。

恆逸PDU系列課程為您提供全面的培訓,包含專案管理實務課程、敏捷管理課程、商業分析課程,也提供ITIL系列課程、藍隊資安、駭客資安、資安專家系列課程、國際標準系列課程,讓您在專業領域中脫穎而出。

更完整PDU課程資料請點選

2024年4月8日 星期一

使用Visual Studio Code設計ASP.NET Core Web API - 2

作者:許薰尹  精誠資訊/恆逸教育訓練中心資深講師

出刊日期:2024/3/20

本文將延續本站《使用Visual Studio Code設計ASP.NET Core Web API - 1》一文的情境,介紹如何在ASP.NET Core Web API專案中設計資料模型、資料服務,以及控制器的程式碼,以處理各種不同的HTTP請求,例如使用 CRUD 操作(新增、讀取、更新、刪除)處理資料。 

設計資料模型

 在設計 Web API 之前,我們需要提供執行作業的資料暫存區,通常會使用模型類別來代表,我們將加入一個「Book」類別代表圖書資料。 模型中定義代表圖書特性的屬性。 模型將應用在 Web API 中傳遞資料,以及用來將圖書資料保存在資料存放區中,在這個文章中我們會將資料放在本機記憶體內部快取之中。
接下來來設計設計「Book」模型。先在終端機視窗執行下列命令,建立「Models」資料夾:

 

1
mkdir Models



在 Visual Studio Code 開發工具中選取「Models」資料夾,並按下「New File」按鈕,新增一個名為「Book.cs」的新檔案。

 

 

圖 1:新增「Book.cs」檔案。


新增下列程式碼到「Book.cs」之中:

 

 

1
2
3
4
5
6
7
8
9
10
11
12
namespace MyWebAPI.Models
{
  public class Book
  {
    public int Id { get; set; }
    public string Title { get; set; }
    public decimal Price { get; set; }
    public DateTime PublishDate { get; set; }
    public bool InStock { get; set; }
    public string Description { get; set; }
  }
}

 

 

設計資料服務


在終端機視窗執行下列命令來建立「Services」資料夾:

1
mkdir Services



在 Visual Studio Code 開發工具中選取「Services」資料夾,並新增一個名為「BookService.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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
using System;
using System.Collections.Generic;
using MyWebAPI.Models;
 
namespace MyWebAPI.Services
{
  public static class BookService
  {
    public static List<Book> Books { get; private set; }
 
    static BookService()
    {
      Books = new List<Book>() {
        new Book() {
           Id = 1 ,
           Title = " Essential Programming Language " ,
           Price = 250 ,
           PublishDate = new DateTime( 2019 ,1,2 ) ,
           InStock = true ,
           Description = "Essential Programming Language "  ,
         },
         new Book() {
           Id = 2 ,
           Title = " Telling Arts " ,
           Price = 245 ,
           PublishDate = new DateTime( 2019 , 4 , 15 ) ,
           InStock = true ,
           Description = " Telling Arts "  ,
         },
           new Book() {
           Id = 3 ,
           Title = " Marvel " ,
           Price = 150  ,
           PublishDate = new DateTime( 2019 , 2, 21 ) ,
           InStock = true ,
           Description = " Marvel "  ,
         },
          new Book() {
           Id = 4 ,
           Title = " The Beauty of Cook" ,
           Price = 450 ,
           PublishDate = new DateTime( 2019 ,12,2 ) ,
           InStock = true ,
           Description = " The Beauty of Cook "  ,
         }
    };
    }
 
     
    public static List<Book> GetAllBooks()
    {
      return Books;
    }
    public static Book? GetBookById( int id )
    {
      return Books.Find( book => book.Id == id );
    }
     
    public static void AddBook( Book book )
    {
      int newId = Books.Count + 1;
      book.Id = newId;
      Books.Add( book );
    }
     
    public static void DeleteBookById( int id )
    {
      Book bookToRemove = Books.Find( book => book.Id == id );
      if ( bookToRemove != null )
      {
        Books.Remove( bookToRemove );
      }
    }
 
    public static void UpdateBook( int id, Book book )
    {
      Book bookToUpdate = Books.Find( book => book.Id == id );
      if ( bookToUpdate != null )
      {
        bookToUpdate.Title = book.Title;
        bookToUpdate.Price = book.Price;
        bookToUpdate.PublishDate = book.PublishDate;
        bookToUpdate.InStock = book.InStock;
        bookToUpdate.Description = book.Description;
      }
    }  
  }
}

 


在終端機視窗命令提示字元執行下列命令,以建置應用程式:

 

1
dotnet build

 

這個命令的執行結果請參考下圖所示:



 



圖 2:建置應用程式。

 


設計控制器


在ASP.NET Core Web API中,控制器是一個用來處理來自用戶端的HTTP請求的類別。每個控制器可以包含一個或多個動作(方法),這些動作負責處理各種不同的HTTP請求,例如GET、POST、PUT等。

在 Visual Studio Code 開發工具中選取「Controllers」資料夾,並新增一個名為「BookController.cs」的新檔案,依照慣例,控制器類別名稱的字尾會加上 「Controller」字串。在「BookController.cs」檔案中加入以下程式碼:

 

1
2
3
4
5
6
7
8
9
10
11
12
using System;
using Microsoft.AspNetCore.Mvc;
 
namespace MyWebAPI.Controllers
{
  [ApiController]
  [Route( "[controller]" )]
  public class BookController : ControllerBase
  {
 
  }
}

 

 


控制器是一個公開類別,通常繼承自「ControllerBase」類別,類別上方套用[ApiController] 和 [Route]。


取得所有圖書資料


控制器類別包含處理不同HTTP請求的方法,這些方法稱為「動作」,它們是公開方法,用來對應特定的HTTP請求。
我們要撰寫的第一個REST指令動詞為「GET」,讓用戶端能夠從API獲取所有圖書的資訊。我們可以利用內建的[HttpGet]屬性,來定義一個方法,負責從服務取回圖書清單,在「BookController」類別加入以下程式碼:

1
2
3
4
5
6
//Get All Books
    [HttpGet]
    public IActionResult GetAll()
    {
      return Ok( MyWebAPI.Services.BookService.GetAllBooks() );
    }


上述程式具有以下特點:

  • 僅回應標識[HttpGet]屬性的HTTP GET請求。
  • 回傳一個List 類型的IActionResult實例。
  •  「GetAll」動作會叫用服務來查詢所有圖書,並自動設定「Content-Type」為「application/json」來回傳資料。


讀取單一圖書資料

 
用戶端可能會需要查詢特定圖書的詳細資料,而不是整個圖書清單。為此,我們可以設計一個帶有「id」參數的「GET」動作。使用內建的[HttpGet("{id}")]屬性定義一個方法,從服務中取回特定的圖書資料。在「BookController」類別加入以下程式碼:

1
2
3
4
5
6
7
//Get Book by Id
    [HttpGet("{id}")]
    public ActionResult<Book> Get( int id )
    {
      var book = MyWebAPI.Services.BookService.GetBookById( id );
      return book != null ? Ok( book ) : NotFound( );
    }

 




Web API的路由系統會根據是否提供id,來決定叫用[HttpGet](無id)和[HttpGet("{id}")](帶有id)兩個方法。

建置並測試控制器資料查詢

 在終端機視窗命令提示字元執行下列命令以執行Web API程式:

1
dotnet run


這個命令的執行結果請參考下圖所示:

 

 

 

圖 3:執行Web API程式。

 
使用 .HTTP 檔案測試控制器

 
開啟 「MyWebAPI.http」檔案,在分隔符「###」下方加入以下兩個「GET」程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@MyWebAPI_HostAddress = http://localhost:5052
 
GET {{MyWebAPI_HostAddress}}/weatherforecast/
Accept: application/json
 
###
GET {{MyWebAPI_HostAddress}}/Book/
Accept: application/json
 
###
GET {{MyWebAPI_HostAddress}}/Book/1
Accept: application/json
 
###




選取「GET」上方的 「Send Request」命令傳送要求,此命令將會以 JSON 格式傳回所有圖書的清單,這個命令的執行結果請參考下圖所示:

 

 


圖 4:送出GET請求取回所有圖書資料。


按相同的方式測試最後一個「GET」請求,取回一本圖書資料,這個命令的執行結果請參考下圖所示:

 


圖 5:送出「GET」請求取「id」相符的圖書資料。




我們的 API 也會處理資料不存在的情況。 若再次呼叫 API,故意使用下列命令傳入無效的圖書id編號:

 

1
2
3
4
5
6
@MyWebAPI_HostAddress = http://localhost:5052
 
###
GET {{MyWebAPI_HostAddress}}/Book/100
Accept: application/json
###


這時將會傳回 「404 Not Found」錯誤,請參考下圖所示:

 

 

圖 6:「404 Not Found」 錯誤。


使用HTTP REPL 命令測試控制器

 
在 Visual Studio Code 開發工具中選擇「 Terminal 」> 「New Terminal」開啟一個新的整合式終端機,在其中輸入並執行命令,以 .NET HTTP REPL 命令列工具,來送出HTTP 請求:

1
connect http://localhost:5052


這個命令的執行結果請參考下圖所示:

 

 

圖 7:使用.NET HTTP REPL命令測試控制器。

 

執行下列命令,查看端點:

1
 


執行下列命令,以前往「Book」端點:

1
cd Book


使用下列命令,透過「HttpRepl」送出「GET」請求:

1
Get


這些命令的執行結果請參考下圖所示:


 

 

圖 8:使用HTTP REPL 命令測試控制器。



ASP.NET Core中的CRUD動作

 
我們的圖書服務提供支援圖書清單的 CRUD(建立、讀取、更新、刪除)操作。這些操作是透過不同的HTTP指令動詞來實現的,並且在ASP.NET Core中,這些動詞由特定的屬性來對應。例如從服務取得一個或多個項目的HTTP GET指令動詞,會透過使用[HttpGet]屬性來標示相應的動作方法。同樣的道理,[HttpPost]可用來標示資料新增的動作方法;[HttpPut] 可用來標示資料修改的動作方法;[HttpDelete] 可用來標示資料刪除的動作方法;

 
「POST」動作

 
我們已經看過「GET」動作的運作方式, 現在就來深入了解「POST」、「PUT」和 「DELETE」動作。我們先使用「POST」方法,透過 Web API 新增圖書資料,在「BookController」類別加入以下程式碼:

1
2
3
4
5
6
7
// Add Book
    [HttpPost]
    public IActionResult AddBook( Book book )
    {
      MyWebAPI.Services.BookService.AddBook( book );
      return Created( $"/book/{book.Id}", book );
    }

 



「AddBook」方法上標註 [HttpPost],用來處理發送到 Book API 的 HTTP「POST」請求。和「Get」方法回傳圖書清單不同的是,這方法會回傳「IActionResult」。「IActionResult」使用戶端能夠了解請求是否執行成功,若成功便提供新建立的圖書「ID」。利用標準的 HTTP 狀態碼,「IActionResult」確保無論用戶端使用的程式語言或開發平台是什麼,都能夠輕鬆地實現與用戶端的整合。

 
「PUT」動作

 
修改或更新我們圖書的程式類似於之前實作的「POST」方法,但需要搭配使用[HttpPut]屬性。除此之外,除了需要更新的「Book」物件之外,還需要一個「id」參數。接著,使用「PUT」方法,透過 Web API 更新圖書。在「BookController」類別加入以下程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Update Book
    [HttpPut("{id}")]
    public IActionResult UpdateBook( int id, Book book )
    {
      var existingBook = MyWebAPI.Services.BookService.GetBookById( id );
      if (existingBook == null)
      {
        return NotFound();
      }
      book.Id = id;
      MyWebAPI.Services.BookService.UpdateBook( id, book );
      return NoContent();
    }

 

 



「DELETE」動作

 
Web API其中一個較簡單要實作的動作是「DELETE」動作,只接受圖書的「id」參數,以便從記憶體內部快取中移除。在「BookController」類別加入以下程式碼:

 

1
2
3
4
5
6
7
8
9
10
11
12
// Delete Book
    [HttpDelete("{id}")]
    public IActionResult DeleteBook( int id )
    {
      var existingBook = MyWebAPI.Services.BookService.GetBookById( id );
      if ( existingBook == null )
      {
        return NotFound();
      }
      MyWebAPI.Services.BookService.DeleteBookById( id );
      return NoContent();
    }

 

 

建置並執行完成的 Web API

 
我們已經完成Web API的設計了,現在讓我們來測試看看。在終端機視窗命令提示字元執行下列命令,建立並啟動 Web API:

1
dotnet run


然後使用「.HTTP」檔案測試已完成的 Web API,重新開啟「MyWebAPI.http」 檔案,使用下列命令來送出「POST」要求,以新增圖書:

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@MyWebAPI_HostAddress = http://localhost:5052
 
POST {{MyWebAPI_HostAddress}}/Book/
Content-Type: application/json
Accept: application/json
 
{
  "id": 100,
  "title": "Book 100",
  "price": 100,
  "PublishDate" : "2021-01-01T00:00:00",
  "InStock": true,
  "Description": "Book 100 Description"
}
 
###


上述命令會傳回新建立的圖書,執行結果請參考下圖所示:

 

 

圖 9:新增圖書。

 

在終端機視窗命令提示字元執行下列命令,以 PUT要求來更新圖書:

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@MyWebAPI_HostAddress = http://localhost:5052
 
PUT {{MyWebAPI_HostAddress}}/Book/1
Content-Type: application/json
Accept: application/json
 
{
  "id": 1,
  "title": "New Book 1",
  "price": 100,
  "PublishDate" : "2021-01-01T00:00:00",
  "InStock": false,
  "Description": "New Book 1 Description"
}
###


上述命令會傳回下列訊息,表示成功:

1
2
3
4
HTTP/1.1 204 No Content
Connection: close
Date: Thu, 29 Feb 2024 05:51:06 GMT
Server: Kestrel


這個命令的執行結果請參考下圖所示:

 

 

圖 10:修改資料。

 

在終端機視窗命令提示字元執行下列命令,以透過「DELETE」動作來刪除圖書:

 

1
2
3
4
5
@MyWebAPI_HostAddress = http://localhost:5052
 
###
 
DELETE {{MyWebAPI_HostAddress}}/Book/1

 

 



上述命令會傳回下列訊息,表示成功,請參考下圖所示:


 

圖 11:刪除圖書。

 

使用HTTP REPL命令測試已完成的 Web API

 
先在專案根目錄加入一個「data.json」檔案,包含要新增的資料,使用JSON格式:

1
{ "id" : 100, "title" : "Book 100", "price" : 100, "PublishDate" : "2021-01-01T00:00:00", "InStock" : true, "Description" : "Book 100 Description"}



在 Visual Studio Code 開發工具 中選擇 >「 Terminal 」> 「New Terminal」開啟一個新的整合式終端機,在其中輸入並執行命令:

1
httprepl http://localhost:5052


執行下列命令,查看端點:

1
 


這些命令的執行結果請參考下圖所示:


 

 

圖 12:httprepl連線到服務。

 
執行下列命令,以前往「Book」端點:

1
cd Book


使用下列命令,透過「HttpRepl」送出 POST請求,利用「-f」參數指定資料所在的檔案:

1
POST -f data.json


這些命令的執行結果請參考下圖所示:

 

 

圖 13:新增資料。

 
接著我們修改「data.json」檔案,修改編號「1」的圖書「title」等資料:

1
{ "id" : 1, "title" : "Book 101", "price" : 100, "PublishDate" : "2021-01-01T00:00:00", "InStock" : true, "Description" : "Book 101 Description" }




重複之前的動作,在 Visual Studio Code 開發工具 中選擇 >「 Terminal 」> 「New Terminal」開啟一個新的整合式終端機,在其中輸入並執行命令:

1
httprepl http://localhost:5052


執行下列命令,查看端點:

1
 

這個命令的執行結果請參考下圖所示:


 

 

 

圖 14:httprepl連線到服務。

 
執行下列命令,以前往「Book」端點:

1
cd Book

執行下列命令,查看端點:
1
 

這時你應該可以看到一個{id}端點,請參考下圖所示:

 

圖 15:{id}端點。

 
使用下列命令,透過「HttpRepl」中送出「PUT」請求:

 

1
Put -f data.json

 
使用「get」命令取回修改結果,比對資料是否正確被修改:
 

1
get

這些命令的執行結果請參考下圖所示:


 

圖 16:修改資料。

 
重複之前的動作,在終端機視窗輸入並執行命令,刪除編號為「1」的資料:

1
delete 1

使用「get」命令取回圖書資料,比對資料是否正確被修改:
1
get

這些命令的執行結果請參考下圖所示: 




 

圖 17:刪除資料。

總結

 
在這一系列文章,我們介紹了如何在 .NET 平台上建立一個運行ASP.NET Core Web API的應用程式。這個Web API利用快取來建立、讀取、修改和刪除圖書資料。現在你已掌握了以下使用ASP.NET Core建立Web API的關鍵步驟:

  •  利用ASP.NET Core Web API範本建立一個Web API應用程式。
  •  建立一個繼承自ControllerBase類別的類別,該類別包含用於響應HTTP請求的方法。
  •  使用工具來測試Web API的運作。