Web API 實作

精心設計的 RESTful Web API 會定義用戶端應用程式可存取的資源、關聯性和瀏覽配置。 當您實作和部署 Web API 時,您應該考慮裝載 Web API 的環境實體需求,以及建構 Web API 的方式,而不是數據的邏輯結構。 本指南著重於實作 Web API 併發布 Web API 的最佳做法,使其可供用戶端應用程式使用。 如需 Web API 設計的詳細資訊,請參閱 Web API 設計

處理要求

當您實作程式代碼來處理要求時,請考慮下列幾點。

GET、PUT、DELETE、HEAD 和 PATCH 動作應該是等冪

實作這些要求的程式代碼不應強加任何副作用。 在相同資源上重複相同的要求應該會產生相同的狀態。 例如,將多個 DELETE 要求傳送至相同的 URI 應該具有相同的效果,不過回應消息中的 HTTP 狀態代碼可能不同。 第一個 DELETE 要求可能會傳回狀態代碼 204 (沒有內容),而後續的 DELETE 要求可能會傳回狀態代碼 404 (找不到)。

注意

Jonathan Oliver 部落格上的等冪模式一文提供等冪性概觀,以及它與數據管理作業的關係。

建立新資源的 POST 動作不應該有不相關的副作用

如果 POST 要求的目的是要建立新的資源,則要求的效果應限制為新的資源(如果涉及某種連結,則可能是任何直接相關的資源)。 例如,在電子商務系統中,為客戶建立新訂單的 POST 要求也可能修改庫存層級併產生帳單資訊,但不應修改與訂單無關的資訊,或對系統整體狀態產生任何其他副作用。

避免實作閒聊 POST、PUT 和 DELETE 作業

支援透過資源集合的 POST、PUT 和 DELETE 要求。 POST 要求可以包含多個新資源的詳細數據,並將其全部新增至相同的集合,PUT 要求可以取代集合中的整個資源集,而 DELETE 要求可以移除整個集合。

ASP.NET Web API 2 中包含的 OData 支援可讓您批次要求。 用戶端應用程式可以封裝數個 Web API 要求,並以單一 HTTP 要求將它們傳送至伺服器,並接收包含每個要求回復的單一 HTTP 回應。 如需詳細資訊,請參閱 Web API 和 Web API OData 中的批次支援簡介。

傳送回應時,請遵循 HTTP 規格

Web API 必須傳回包含正確 HTTP 狀態代碼的訊息,讓用戶端判斷如何處理結果、適當的 HTTP 標頭,讓用戶端了解結果的性質,以及適當格式化的本文,讓客戶端能夠剖析結果。

例如,POST 作業應該傳回狀態代碼 201(已建立),而回應消息應該會在回應訊息的 Location 標頭中包含新建立資源的 URI。

支援內容交涉

回應訊息的本文可能包含各種格式的數據。 例如,HTTP GET 要求可以傳回 JSON 或 XML 格式的數據。 當用戶端提交要求時,它可以包含 Accept 標頭,指定它可以處理的數據格式。 這些格式會指定為媒體類型。 例如,發出擷取影像的 GET 要求用戶端可以指定 Accept 標頭,列出用戶端可以處理的媒體類型,例如 image/jpeg, image/gif, image/png。 當 Web API 傳回結果時,它應該使用下列其中一種媒體類型來格式化數據,並在回應的 Content-Type 標頭中指定格式。

如果用戶端未指定 Accept 標頭,則請針對回應本文使用合理的預設格式。 例如,ASP.NET Web API 架構預設為以文字為基礎的數據 JSON。

HATEOAS 方法可讓用戶端從初始起點巡覽和探索資源。 這是使用包含 URI 的連結來達成此目的;當用戶端發出 HTTP GET 要求以取得資源時,回應應包含可讓用戶端應用程式快速找出任何直接相關資源的 URI。 例如,在支援電子商務解決方案的 Web API 中,客戶可能已訂購許多訂單。 當用戶端應用程式擷取客戶的詳細數據時,回應應包含可讓用戶端應用程式傳送可擷取這些訂單的 HTTP GET 要求的連結。 此外,HATEOAS 樣式的連結應該描述每個鏈接資源所支援的其他作業(POST、PUT、DELETE 等等),並搭配對應的 URI 來執行每個要求。 這個方法在 API 設計會更詳細地說明。

目前沒有任何標準可控管 HATEOAS 的實作,但下列範例說明一個可能的方法。 在此範例中,尋找客戶詳細數據的 HTTP GET 要求會傳回回應,其中包含參考該客戶的訂單的 HATEOAS 連結:

GET https://adventure-works.com/customers/2 HTTP/1.1
Accept: text/json
...
HTTP/1.1 200 OK
...
Content-Type: application/json; charset=utf-8
...
Content-Length: ...
{"CustomerID":2,"CustomerName":"Bert","Links":[
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"GET",
    "types":["text/xml","application/json"]},
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"PUT",
    "types":["application/x-www-form-urlencoded"]},
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"DELETE",
    "types":[]},
    {"rel":"orders",
    "href":"https://adventure-works.com/customers/2/orders",
    "action":"GET",
    "types":["text/xml","application/json"]},
    {"rel":"orders",
    "href":"https://adventure-works.com/customers/2/orders",
    "action":"POST",
    "types":["application/x-www-form-urlencoded"]}
]}

在此範例中,客戶數據會以 Customer 下列代碼段所示的 類別表示。 HATEOAS 連結會保留在集合屬性中 Links

public class Customer
{
    public int CustomerID { get; set; }
    public string CustomerName { get; set; }
    public List<Link> Links { get; set; }
    ...
}

public class Link
{
    public string Rel { get; set; }
    public string Href { get; set; }
    public string Action { get; set; }
    public string [] Types { get; set; }
}

HTTP GET 作業會從記憶體擷取客戶數據並建構 Customer 物件,然後填入 Links 集合。 結果會格式化為 JSON 回應消息。 每個連結都包含下列欄位:

  • 要傳回的對象與連結所描述之對象之間的關聯性 。Rel。 在此情況下 self ,表示連結是物件本身的參考(類似於 this 許多面向物件語言的指標),而且 orders 是包含相關順序資訊的集合名稱。
  • 連結以 URI 形式描述之物件的超連結 (Href)。
  • 可以傳送至此 URI 的 HTTP 要求類型 (Action)。
  • HTTP 要求中應該提供的任何數據格式,Types或根據要求的類型,可以在響應中傳回。

範例 HTTP 回應中顯示的 HATEOAS 連結表示用戶端應用程式可以執行下列作業:

  • 對 URI https://adventure-works.com/customers/2 的 HTTP GET 要求,以擷取客戶的詳細數據(再次)。 數據可以傳回為 XML 或 JSON。
  • URI https://adventure-works.com/customers/2 的 HTTP PUT 要求,可修改客戶的詳細數據。 新的數據必須以 x-www-form-urlencoded 格式的要求訊息提供。
  • 要刪除客戶的 URI https://adventure-works.com/customers/2 的 HTTP DELETE 要求。 要求不需要任何額外的資訊,也不會在回應消息本文中傳回數據。
  • URI https://adventure-works.com/customers/2/orders 的 HTTP GET 要求,以尋找客戶的所有訂單。 數據可以傳回為 XML 或 JSON。
  • URI https://adventure-works.com/customers/2/orders 的 HTTP POST 要求,可為此客戶建立新訂單。 數據必須以 x-www-form-urlencoded 格式的要求訊息提供。

處理例外狀況

如果作業擲回未攔截的例外狀況,請考慮下列幾點。

擷取例外狀況,並傳回對用戶端有意義的回應

實作 HTTP 作業的程式代碼應該提供完整的例外狀況處理,而不是讓未攔截的例外狀況傳播至架構。 如果例外狀況無法順利完成作業,則例外狀況可以在回應訊息中傳回,但應該包含造成例外狀況之錯誤的有意義描述。 例外狀況也應該包含適當的 HTTP 狀態代碼,而不是只針對每個情況傳回狀態代碼 500。 例如,如果使用者要求造成違反條件約束的資料庫更新(例如嘗試刪除有未處理訂單的客戶),您應該傳回狀態代碼 409 (衝突)和訊息本文,指出衝突的原因。 如果其他一些條件無法解析要求,您可以傳回狀態代碼 400 (不正確的要求)。 您可以在 W3C 網站上的 [狀態代碼定義] 頁面上找到 HTTP 狀態代碼的完整清單。

程式代碼範例會捕捉不同的條件,並傳回適當的回應。

[HttpDelete]
[Route("customers/{id:int}")]
public IHttpActionResult DeleteCustomer(int id)
{
    try
    {
        // Find the customer to be deleted in the repository
        var customerToDelete = repository.GetCustomer(id);

        // If there is no such customer, return an error response
        // with status code 404 (Not Found)
        if (customerToDelete == null)
        {
            return NotFound();
        }

        // Remove the customer from the repository
        // The DeleteCustomer method returns true if the customer
        // was successfully deleted
        if (repository.DeleteCustomer(id))
        {
            // Return a response message with status code 204 (No Content)
            // To indicate that the operation was successful
            return StatusCode(HttpStatusCode.NoContent);
        }
        else
        {
            // Otherwise return a 400 (Bad Request) error response
            return BadRequest(Strings.CustomerNotDeleted);
        }
    }
    catch
    {
        // If an uncaught exception occurs, return an error response
        // with status code 500 (Internal Server Error)
        return InternalServerError();
    }
}

提示

請勿包含可能對嘗試滲透 API 的攻擊者有用的資訊。

許多網頁伺服器會在到達 Web API 之前自行設陷錯誤狀況。 例如,如果您為網站設定驗證,且用戶無法提供正確的驗證資訊,Web 伺服器應該響應狀態代碼 401 (未經授權)。 一旦客戶端經過驗證,您的程式代碼就可以執行自己的檢查,以確認用戶端是否應該能夠存取所要求的資源。 如果此授權失敗,您應該傳回狀態代碼 403(禁止)。

一致地處理例外狀況,並記錄錯誤的相關信息

若要以一致的方式處理例外狀況,請考慮在整個 Web API 中實作全域錯誤處理策略。 您也應該納入錯誤記錄,以擷取每個例外狀況的完整詳細數據;只要客戶端無法透過 Web 存取此錯誤記錄檔,就可以包含詳細資訊。

區分客戶端錯誤和伺服器端錯誤

HTTP 通訊協定會區分因用戶端應用程式(HTTP 4xx 狀態代碼)所發生的錯誤,以及伺服器發生錯誤所造成的錯誤(HTTP 5xx 狀態代碼)。 請確定在任何錯誤回應訊息中都遵守此慣例。

優化客戶端數據存取

在分散式環境中,例如涉及 Web 伺服器和用戶端應用程式的分散式環境中,其中一個主要來源是網路。 這可能會成為相當的瓶頸,特別是用戶端應用程式經常傳送要求或接收數據時。 因此,您應該將流量流向網路的流量降到最低。 當您實作程式代碼以擷取和維護數據時,請考慮下列幾點:

支援用戶端快取

HTTP 1.1 通訊協定支援在用戶端和中繼伺服器中快取,要求是透過快取控制標頭路由傳送。 當用戶端應用程式將 HTTP GET 要求傳送至 Web API 時,回應可以包含 Cache-Control 標頭,指出用戶端或中繼伺服器是否可以安全地快取回應主體中的數據,以及要求經過路由傳送的時間,以及該要求到期前多久並視為過期。

下列範例顯示 HTTP GET 要求和包含 Cache-Control 標頭的對應回應:

GET https://adventure-works.com/orders/2 HTTP/1.1
HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: text/json; charset=utf-8
Content-Length: ...
{"orderID":2,"productID":4,"quantity":2,"orderValue":10.00}

在此範例中,Cache-Control 標頭會指定傳回的數據應該在 600 秒後過期,而且僅適用於單一用戶端,而且不得儲存在其他用戶端使用的共用快取中(這是 私用的)。 Cache-Control 標頭可以指定公用,而不是用,在此情況下,數據可以儲存在共用快取中,也可以指定不存放區,在此情況下,用戶端不得快取數據。 下列程式代碼範例示範如何在回應消息中建構 Cache-Control 標頭:

public class OrdersController : ApiController
{
    ...
    [Route("api/orders/{id:int:min(0)}")]
    [HttpGet]
    public IHttpActionResult FindOrderByID(int id)
    {
        // Find the matching order
        Order order = ...;
        ...
        // Create a Cache-Control header for the response
        var cacheControlHeader = new CacheControlHeaderValue();
        cacheControlHeader.Private = true;
        cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);
        ...

        // Return a response message containing the order and the cache control header
        OkResultWithCaching<Order> response = new OkResultWithCaching<Order>(order, this)
        {
            CacheControlHeader = cacheControlHeader
        };
        return response;
    }
    ...
}

此程式代碼會使用名為的OkResultWithCaching自定義IHttpActionResult類別。 這個類別可讓控制器設定快取標頭內容:

public class OkResultWithCaching<T> : OkNegotiatedContentResult<T>
{
    public OkResultWithCaching(T content, ApiController controller)
        : base(content, controller) { }

    public OkResultWithCaching(T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
        : base(content, contentNegotiator, request, formatters) { }

    public CacheControlHeaderValue CacheControlHeader { get; set; }
    public EntityTagHeaderValue ETag { get; set; }

    public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response;
        try
        {
            response = await base.ExecuteAsync(cancellationToken);
            response.Headers.CacheControl = this.CacheControlHeader;
            response.Headers.ETag = ETag;
        }
        catch (OperationCanceledException)
        {
            response = new HttpResponseMessage(HttpStatusCode.Conflict) {ReasonPhrase = "Operation was cancelled"};
        }
        return response;
    }
}

注意

HTTP 通訊協定也會定義 Cache-Control 標頭的 no-cache 指示詞。 相當令人困惑的是,這個指示詞並不表示「不要快取」,而是「先向伺服器重新驗證快取的資訊,再傳回它」;數據仍可快取,但每次用來確保數據仍為最新狀態時,都會加以檢查。

快取管理是用戶端應用程式或中繼伺服器的責任,但如果正確實作,它可以節省頻寬並改善效能,方法是移除擷取最近擷取的數據的需求。

Cache-Control 標頭中的最大存留期 值只是一個指南,而且不保證對應數據不會在指定時間內變更。 Web API 應該根據數據的預期波動性,將最大存留期設定為適當的值。 當此期間到期時,客戶端應該捨棄快取中的物件。

注意

大部分的新式網頁瀏覽器都支援用戶端快取,方法是將適當的快取控制標頭新增至要求,並檢查結果的標頭,如所述。 不過,某些較舊的瀏覽器不會快取從包含查詢字串的 URL 傳回的值。 這通常不是自定義用戶端應用程式的問題,這些用戶端應用程式會根據這裡所討論的通訊協定來實作自己的快取管理策略。

某些較舊的 Proxy 會表現出相同的行為,而且可能不會根據具有查詢字串的 URL 快取要求。 這可能是透過這類 Proxy 連線至網頁伺服器的自定義用戶端應用程式的問題。

提供ETag以優化查詢處理

當用戶端應用程式擷取物件時,回應消息也可以包含 ETag (實體卷標)。 ETag 是表示資源版本的不透明字串;每次資源變更 ETag 時,也會修改。 用戶端應用程式應該快取此 ETag 作為數據的一部分。 下列程式代碼範例示範如何將 ETag 新增為 HTTP GET 要求的回應的一部分。 此程式代碼會使用 GetHashCode 物件的 方法來產生識別物件的數值(如有必要,您可以覆寫此方法,並使用 MD5 之類的演算法產生您自己的哈希:

public class OrdersController : ApiController
{
    ...
    public IHttpActionResult FindOrderByID(int id)
    {
        // Find the matching order
        Order order = ...;
        ...

        var hashedOrder = order.GetHashCode();
        string hashedOrderEtag = $"\"{hashedOrder}\"";
        var eTag = new EntityTagHeaderValue(hashedOrderEtag);

        // Return a response message containing the order and the cache control header
        OkResultWithCaching<Order> response = new OkResultWithCaching<Order>(order, this)
        {
            ...,
            ETag = eTag
        };
        return response;
    }
    ...
}

Web API 所張貼的回應消息如下所示:

HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: text/json; charset=utf-8
ETag: "2147483648"
Content-Length: ...
{"orderID":2,"productID":4,"quantity":2,"orderValue":10.00}

提示

基於安全性考慮,不允許透過已驗證的 (HTTPS) 連線傳回的敏感數據或數據進行快取。

用戶端應用程式可以發出後續的 GET 要求,隨時擷取相同的資源,而且如果資源已變更(它有不同的 ETag),則應該捨棄快取的版本,以及新增至快取的新版本。 如果資源很大,而且需要大量的頻寬才能傳輸回用戶端,則擷取相同數據的重複要求可能會變得效率不佳。 為了對抗這種情況,HTTP 通訊協定會定義下列程式,以優化您應該在 Web API 中支援的 GET 要求:

  • 用戶端會針對 If-None-Match HTTP 標頭中參考的資源目前快取版本,建構包含 ETag 的 GET 要求:

    GET https://adventure-works.com/orders/2 HTTP/1.1
    If-None-Match: "2147483648"
    
  • Web API 中的 GET 作業會取得所要求數據的目前 ETag(上述範例中的順序 2),並將它與 If-None-Match 標頭中的值進行比較。

  • 如果要求的目前 ETag 符合要求的 ETag,則資源尚未變更,且 Web API 應該傳回 HTTP 回應,其中包含空白訊息本文和狀態代碼 304(未修改)。

  • 如果要求的目前 ETag 與要求的 ETag 不相符,則數據已變更,而 Web API 應該傳回 HTTP 回應,其中包含訊息本文中的新數據,以及狀態代碼 200 (確定)。

  • 如果要求的數據已不存在,則 Web API 應該傳回 HTTP 回應,狀態代碼為 404 (找不到)。

  • 用戶端會使用狀態代碼來維護快取。 如果數據未變更(狀態代碼 304),則物件可以保持快取狀態,用戶端應用程式應該繼續使用這個版本的物件。 如果數據已變更(狀態代碼 200),則應該捨棄快取的物件,並插入新的物件。 如果數據已無法使用(狀態代碼 404),則應該從快取中移除物件。

注意

如果響應標頭包含 Cache-Control 標頭 no-store,則不論 HTTP 狀態代碼為何,應該一律從快取中移除物件。

下列程式代碼顯示 FindOrderByID 擴充以支援 If-None-Match 標頭的方法。 請注意,如果省略 If-None-Match 標頭,則一律會擷取指定的順序:

public class OrdersController : ApiController
{
    [Route("api/orders/{id:int:min(0)}")]
    [HttpGet]
    public IHttpActionResult FindOrderByID(int id)
    {
        try
        {
            // Find the matching order
            Order order = ...;

            // If there is no such order then return NotFound
            if (order == null)
            {
                return NotFound();
            }

            // Generate the ETag for the order
            var hashedOrder = order.GetHashCode();
            string hashedOrderEtag = $"\"{hashedOrder}\"";

            // Create the Cache-Control and ETag headers for the response
            IHttpActionResult response;
            var cacheControlHeader = new CacheControlHeaderValue();
            cacheControlHeader.Public = true;
            cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);
            var eTag = new EntityTagHeaderValue(hashedOrderEtag);

            // Retrieve the If-None-Match header from the request (if it exists)
            var nonMatchEtags = Request.Headers.IfNoneMatch;

            // If there is an ETag in the If-None-Match header and
            // this ETag matches that of the order just retrieved,
            // then create a Not Modified response message
            if (nonMatchEtags.Count > 0 &&
                String.CompareOrdinal(nonMatchEtags.First().Tag, hashedOrderEtag) == 0)
            {
                response = new EmptyResultWithCaching()
                {
                    StatusCode = HttpStatusCode.NotModified,
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag
                };
            }
            // Otherwise create a response message that contains the order details
            else
            {
                response = new OkResultWithCaching<Order>(order, this)
                {
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag
                };
            }

            return response;
        }
        catch
        {
            return InternalServerError();
        }
    }
...
}

此範例會納入名為EmptyResultWithCaching的其他自定義IHttpActionResult類別。 這個類別只會做為不包含響應主體之對象的包裝 HttpResponseMessage 函式:

public class EmptyResultWithCaching : IHttpActionResult
{
    public CacheControlHeaderValue CacheControlHeader { get; set; }
    public EntityTagHeaderValue ETag { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public Uri Location { get; set; }

    public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage(StatusCode);
        response.Headers.CacheControl = this.CacheControlHeader;
        response.Headers.ETag = this.ETag;
        response.Headers.Location = this.Location;
        return response;
    }
}

提示

在此範例中,數據的 ETag 是藉由哈希從基礎數據源擷取的數據來產生。 如果 ETag 可以以其他方式計算,則可以進一步優化進程,而且只有在數據源已變更時,才需要從數據源擷取數據。 如果數據很大或存取數據源,此方法特別有用(例如,如果數據源是遠端資料庫)。。

使用ETag支援開放式並行存取

若要透過先前快取的數據啟用更新,HTTP 通訊協定支持開放式並行策略。 如果在擷取和快取資源之後,用戶端應用程式接著傳送 PUT 或 DELETE 要求來變更或移除資源,則應該包含參考 ETag 的 If-Match 標頭。 接著,Web API 可以使用這項資訊來判斷資源自擷取後是否已由其他用戶變更,並將適當的回應傳回用戶端應用程式,如下所示:

  • 用戶端會建構 PUT 要求,其中包含資源的新詳細數據,以及 If-Match HTTP 標頭中所參考資源目前快取版本的 ETag。 下列範例顯示更新訂單的PUT要求:

    PUT https://adventure-works.com/orders/1 HTTP/1.1
    If-Match: "2282343857"
    Content-Type: application/x-www-form-urlencoded
    Content-Length: ...
    productID=3&quantity=5&orderValue=250
    
  • Web API 中的 PUT 作業會取得所要求數據的目前 ETag(上述範例中的順序 1),並將它與 If-Match 標頭中的值進行比較。

  • 如果要求的目前 ETag 符合要求的 ETag,則資源尚未變更,且 Web API 應該執行更新,如果成功,則會傳回 HTTP 狀態代碼為 204(沒有內容)的訊息。 回應可以包含資源更新版本的 Cache-Control 和 ETag 標頭。 回應應該一律包含參考新更新資源 URI 的 Location 標頭。

  • 如果要求的目前 ETag 與要求的 ETag 不符,則數據已由其他使用者變更,因為擷取數據,而 Web API 應該傳回 HTTP 回應,其中包含空白訊息本文和狀態代碼 412 (前置條件失敗)。

  • 如果要更新的資源已不存在,則 Web API 應該傳回 HTTP 回應,狀態代碼為 404 (找不到)。

  • 用戶端會使用狀態代碼和回應標頭來維護快取。 如果數據已更新(狀態代碼 204),則物件可以保持快取(只要 Cache-Control 標頭未指定無存放區),但應該更新 ETag。 如果另一位使用者已變更數據(狀態代碼 412)或找不到 (狀態代碼 404),則應該捨棄快取的物件。

下一個程式代碼範例顯示 Orders 控制器的 PUT 作業實作:

public class OrdersController : ApiController
{
    [HttpPut]
    [Route("api/orders/{id:int}")]
    public IHttpActionResult UpdateExistingOrder(int id, DTOOrder order)
    {
        try
        {
            var baseUri = Constants.GetUriFromConfig();
            var orderToUpdate = this.ordersRepository.GetOrder(id);
            if (orderToUpdate == null)
            {
                return NotFound();
            }

            var hashedOrder = orderToUpdate.GetHashCode();
            string hashedOrderEtag = $"\"{hashedOrder}\"";

            // Retrieve the If-Match header from the request (if it exists)
            var matchEtags = Request.Headers.IfMatch;

            // If there is an ETag in the If-Match header and
            // this ETag matches that of the order just retrieved,
            // or if there is no ETag, then update the Order
            if (((matchEtags.Count > 0 &&
                String.CompareOrdinal(matchEtags.First().Tag, hashedOrderEtag) == 0)) ||
                matchEtags.Count == 0)
            {
                // Modify the order
                orderToUpdate.OrderValue = order.OrderValue;
                orderToUpdate.ProductID = order.ProductID;
                orderToUpdate.Quantity = order.Quantity;

                // Save the order back to the data store
                // ...

                // Create the No Content response with Cache-Control, ETag, and Location headers
                var cacheControlHeader = new CacheControlHeaderValue();
                cacheControlHeader.Private = true;
                cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);

                hashedOrder = order.GetHashCode();
                hashedOrderEtag = $"\"{hashedOrder}\"";
                var eTag = new EntityTagHeaderValue(hashedOrderEtag);

                var location = new Uri($"{baseUri}/{Constants.ORDERS}/{id}");
                var response = new EmptyResultWithCaching()
                {
                    StatusCode = HttpStatusCode.NoContent,
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag,
                    Location = location
                };

                return response;
            }

            // Otherwise return a Precondition Failed response
            return StatusCode(HttpStatusCode.PreconditionFailed);
        }
        catch
        {
            return InternalServerError();
        }
    }
    ...
}

提示

If-Match 標頭的使用是完全選擇性的,如果省略 Web API 一律會嘗試更新指定的順序,可能會盲目覆寫其他使用者所做的更新。 若要避免因更新遺失而發生問題,請一律提供 If-Match 標頭。

處理大型要求和回應

有時候,用戶端應用程式需要發出要求,以傳送或接收大小可能為數MB(或更大)的數據。 在傳輸此資料量時等候,可能會導致用戶端應用程式變得沒有回應。 當您需要處理包含大量數據的要求時,請考慮下列幾點:

優化涉及大型物件的要求和回應

某些資源可能是大型物件,或包含大型字段,例如圖形影像或其他二進位數據類型。 Web API 應該支援串流功能,以啟用優化上傳和下載這些資源。

HTTP 通訊協定提供區塊傳輸編碼機制,可將大型數據物件串流回用戶端。 當用戶端傳送大型物件的 HTTP GET 要求時,Web API 可以透過 HTTP 連線分次將回復傳回。 一開始可能不知道回復中的數據長度(可能產生),因此裝載 Web API 的伺服器應該傳送回應訊息,其中包含指定 Transfer-Encoding: Chunked 標頭而非 Content-Length 標頭的每個區塊。 用戶端應用程式可以接著接收每個區塊,以建置完整的回應。 當伺服器以零大小傳回最終區塊時,數據傳輸就會完成。

單一要求可能會造成耗用大量資源的龐大物件。 如果在串流處理期間,Web API 判斷要求中的數據量已超過一些可接受的界限,它可以中止作業,並傳回狀態代碼為 413 的回應消息(要求實體太大)。

您可以使用 HTTP 壓縮,將透過網路傳輸的大型物件大小降到最低。 這種方法有助於減少網路流量和相關聯的網路等待時間,但代價是要求在用戶端和裝載 Web API 的伺服器進行額外處理。 例如,預期接收壓縮數據的用戶端應用程式可以包含 Accept-Encoding: gzip 要求標頭(也可以指定其他數據壓縮演算法)。 如果伺服器支援壓縮,它應該回應訊息本文和 Content-Encoding: gzip 響應標頭中以 gzip 格式保留的內容。

您可以將編碼壓縮與串流結合;先壓縮數據再進行串流處理,並在訊息標頭中指定 gzip 內容編碼和區塊傳輸編碼。 另請注意,不論 Web API 是否壓縮數據,某些網頁伺服器(例如 Internet Information Server)都可以設定為自動壓縮 HTTP 回應。

針對不支援異步操作的用戶端實作部分回應

除了異步串流,用戶端應用程式可以明確要求區塊中大型對象的數據,稱為部分回應。 用戶端應用程式會傳送 HTTP HEAD 要求,以取得對象的相關信息。 如果 Web API 支援部分回應,它應該使用包含 Accept-Ranges 標頭的回應訊息和 Content-Length 表示物件大小總計的標頭來回應 HEAD 要求,但訊息本文應該是空的。 用戶端應用程式可以使用這項資訊來建構一系列 GET 要求,以指定要接收的位元組範圍。 Web API 應該傳回 HTTP 狀態為 206(部分內容)、內容長度標頭,指定回應消息本文中包含的實際數據量,以及指出此數據所代表對象的哪個部分(例如位元組 40008000)的 Content-Range 標頭。

在 API 設計會更詳細地描述 HTTP HEAD 要求和部分回應。

避免在用戶端應用程式中傳送不必要的 100-Continue 狀態消息

即將將大量數據傳送至伺服器的用戶端應用程式,可以先判斷伺服器是否真的願意接受要求。 在傳送數據之前,用戶端應用程式可以提交具有 Expect: 100-Continue 標頭的 HTTP 要求,此標頭會指出數據的大小,但空白訊息本文。 如果伺服器願意處理要求,它應該會回應指定 HTTP 狀態 100 的訊息(繼續)。 用戶端應用程式接著可以繼續並傳送完整的要求,包括訊息本文中的數據。

如果您使用 IIS 裝載服務,HTTP.sys驅動程式會自動偵測並處理預期:100-Continue 標頭,再將要求傳遞至 Web 應用程式。 這表示您不太可能在應用程式程序代碼中看到這些標頭,而且您可以假設 IIS 已篩選任何認為不適合或太大的訊息。

如果您使用 .NET Framework 建置用戶端應用程式,則所有 POST 和 PUT 訊息都會先傳送具有預期:100-Continue 標頭的訊息。 如同伺服器端,.NET Framework 會以透明方式處理進程。 不過,此程式會導致每個 POST 和 PUT 要求造成兩次往返伺服器,即使是小型要求。 如果您的應用程式未傳送具有大量資料的要求,您可以使用 類別在用戶端應用程式中建立ServicePoint物件來停用此功能ServicePointManagerServicePoint對象會根據識別伺服器上資源的 URI 配置和主機片段,處理用戶端對伺服器的連線。 然後,您可以將 對象的 屬性ServicePoint設定Expect100Continue為 false。 用戶端透過符合物件配置和主機片段的 ServicePoint URI 發出的所有後續 POST 和 PUT 要求,將會傳送不含預期:100-Continue 標頭。 下列程式代碼示範如何設定 ServicePoint 物件,以配置 和主機www.contoso.com設定傳送至URI http 的所有要求。

Uri uri = new Uri("https://www.contoso.com/");
ServicePoint sp = ServicePointManager.FindServicePoint(uri);
sp.Expect100Continue = false;

您也可以設定 類別的ServicePointManager靜態Expect100Continue屬性,以針對所有後續建立的 ServicePoint 物件指定這個屬性的預設值。

針對可能傳回大量物件的要求支持分頁

如果集合包含大量資源,向對應的 URI 發出 GET 要求可能會導致在裝載 Web API 影響效能的伺服器上進行大量處理,併產生大量的網路流量,而導致延遲增加。

為了處理這些情況,Web API 應該支持查詢字串,讓用戶端應用程式能夠精簡要求,或擷取更容易管理、離散區塊(或頁面) 中的數據。 下列程式代碼顯示 GetAllOrders 控制器中的 Orders 方法。 此方法會擷取訂單的詳細數據。 如果此方法不受限制,可能會傳回大量數據。 limitoffset 參數的目的是為了將數據量減少為較小的子集,在此案例中,預設只有前 10 個訂單:

public class OrdersController : ApiController
{
    ...
    [Route("api/orders")]
    [HttpGet]
    public IEnumerable<Order> GetAllOrders(int limit=10, int offset=0)
    {
        // Find the number of orders specified by the limit parameter
        // starting with the order specified by the offset parameter
        var orders = ...
        return orders;
    }
    ...
}

用戶端應用程式可以使用 URI https://www.adventure-works.com/api/orders?limit=30&offset=50發出要求,以從位移 50 開始擷取 30 個訂單。

提示

避免讓用戶端應用程式指定查詢字串,以產生長度超過 2000 個字元的 URI。 許多 Web 用戶端和伺服器無法處理這麼長的 URI。

維護回應性、延展性和可用性

在世界各地執行的許多用戶端應用程式都可能會使用相同的 Web API。 請務必確保 Web API 實作,以在負載繁重的情況下維持回應性、可調整以支援高度變化的工作負載,以及保證執行業務關鍵作業的用戶端可用性。 判斷如何符合這些需求時,請考慮下列幾點:

為長時間執行的要求提供異步支援

應該執行可能需要很長的時間來處理的要求,而不會封鎖提交要求的用戶端。 Web API 可以執行一些初始檢查來驗證要求、起始個別的工作來執行工作,然後傳回具有 HTTP 程式代碼 202(已接受)的回應消息。 工作可以在 Web API 處理中以異步方式執行,也可以卸除至背景工作。

Web API 也應該提供將處理結果傳回給用戶端應用程式的機制。 您可以藉由提供用戶端應用程式的輪詢機制,定期查詢處理是否已完成並取得結果,或讓 Web API 在作業完成時傳送通知來達成此目的。

您可以使用下列方法來提供 作為虛擬資源的輪詢 URI,以實作簡單的輪詢 機制:

  1. 用戶端應用程式會將初始要求傳送至 Web API。
  2. Web API 會將要求的相關信息儲存在 Azure 數據表 儲存體Microsoft Azure 快取中保存的數據表中,併產生此專案的唯一索引鍵,可能是 GUID 的形式。 或者,也可以透過 Azure 服務匯流排 傳送包含要求相關信息和唯一索引鍵的訊息。
  3. Web API 會將處理起始為個別工作,或使用 Hangfire 之類的連結庫。 Web API 會將數據表中工作的狀態記錄為 執行中。
    • 如果您使用 Azure 服務匯流排,訊息處理會與 API 分開完成,可能是使用 Azure FunctionsAKS
  4. Web API 會傳回 HTTP 狀態代碼為 202(已接受)的回應消息,以及包含所產生之唯一密鑰的 URI,例如 /polling/{guid}
  5. 當工作完成時,Web API 會將結果儲存在數據表中,並將工作的狀態設定為 [完成]。 請注意,如果工作失敗,Web API 也可以儲存失敗的相關信息,並將狀態設定為 [失敗]。
    • 請考慮套用 重試技術 來解決可能的暫時性失敗。
  6. 當工作執行時,用戶端可以繼續執行自己的處理。 它可以定期將要求傳送至稍早收到的 URI。
  7. URI 上的 Web API 會查詢資料表中對應工作的狀態,並傳回包含此狀態的 HTTP 狀態代碼 200 (OK) 的回應訊息(執行中、 完成失敗)。 如果工作已完成或失敗,回應訊息也可以包含處理的結果,或失敗原因的任何可用資訊。
    • 如果長時間執行的進程具有較多的中繼狀態,最好是使用支援Saga模式的連結庫,例如 NServiceBusMassTransit

實作通知的選項包括:

  • 使用通知中樞將異步回應推送至用戶端應用程式。 如需詳細資訊,請參閱 使用 Azure 通知中樞將通知傳送給特定使用者。
  • 使用 Comet 模型來保留用戶端與裝載 Web API 的伺服器之間的持續性網路連線,並使用此連線將訊息從伺服器推送回用戶端。 MSDN 雜誌文章 :在 Microsoft .NET Framework 中建置簡單的 Comet 應用程式說明範例解決方案。
  • 使用 SignalR 透過永續性網路連線,即時將數據從網頁伺服器推送至用戶端。 SignalR 可用於 ASP.NET Web 應用程式作為 NuGet 套件。 您可以在 ASP.NET SignalR 網站上找到詳細資訊

確定每個要求都是無狀態的

每個要求都應該視為不可部分完成。 用戶端應用程式提出的一個要求與相同用戶端所提交的任何後續要求之間不應有任何相依性。 此方法可協助延展性;Web 服務的實例可以部署在數部伺服器上。 用戶端要求可以導向這些實例中的任何一個,而且結果應該一律相同。 它也會基於類似的原因改善可用性;如果 Web 伺服器失敗的要求可以在伺服器重新啟動時路由傳送至另一個實例(使用 Azure 流量管理員),而不會影響客戶端應用程式。

追蹤客戶端並實作節流以減少 DoS 攻擊的機會

如果特定用戶端在指定時間內提出大量要求,它可能會壟斷服務並影響其他用戶端的效能。 若要減輕此問題,Web API 可以藉由追蹤所有連入要求的IP位址,或記錄每個已驗證的存取,來監視來自用戶端應用程式的呼叫。 您可以使用這項資訊來限制資源存取。 如果客戶端超過定義的限制,Web API 可以傳回狀態為 503(服務無法使用)的回應消息,並包含 Retry-After 標頭,指定用戶端何時可以傳送下一個要求,而不會遭到拒絕。 此策略可協助減少一組阻礙系統之用戶端遭到阻斷服務 (DoS) 攻擊的機會。

小心管理永續性 HTTP 連線

HTTP 通訊協定支援可用的持續性 HTTP 連線。 HTTP 1.0 規格新增了 連線 ion:Keep-Alive 標頭,可讓用戶端應用程式向伺服器指出,它可以使用相同的連線來傳送後續要求,而不是開啟新的要求。 如果用戶端未在主機定義的期間內重複使用連線,連線就會自動關閉。 此行為是 Azure 服務所使用的 HTTP 1.1 中的預設值,因此不需要在訊息中包含 Keep-Alive 標頭。

保持連線開啟有助於藉由減少延遲和網路壅塞來改善回應性,但藉由讓不必要的連線保持開啟的時間超過必要時間,進而限制其他並行用戶端連線的能力,因而不利於延展性。 如果用戶端應用程式在行動裝置上執行,它也會影響電池使用時間;如果應用程式只對伺服器發出偶爾的要求,則維護開啟的連線可能會導致電池更快速地耗盡。 為了確保連線不是使用 HTTP 1.1 持續連線,用戶端可以包含具有訊息的 連線 ion:Close 標頭,以覆寫預設行為。 同樣地,如果伺服器正在處理非常大量的用戶端,它可以在回應訊息中包含 連線 ion:Close 標頭,以關閉連線並儲存伺服器資源。

注意

持續性 HTTP 連線是純粹的選擇性功能,可減少與重複建立通訊通道相關聯的網路額外負荷。 Web API 和用戶端應用程式都不應相依於可用的持續性 HTTP 連線。 請勿使用持續性 HTTP 連線來實作 Comet 樣式通知系統;相反地,您應該在 TCP 層使用套接字(或 Web 套接字)。 最後,請注意,如果用戶端應用程式透過 Proxy 與伺服器通訊,Keep-Alive 標頭會受到限制;只有與用戶端和 Proxy 的連線才會持續。

發佈和管理 Web API

若要讓 Web API 可供用戶端應用程式使用,必須將 Web API 部署至主機環境。 此環境通常是網頁伺服器,雖然可能是一些其他類型的主機進程。 發佈 Web API 時,您應該考慮下列幾點:

  • 所有要求都必須經過驗證和授權,而且必須強制執行適當的訪問控制層級。
  • 商業 Web API 可能會受到有關回應時間的各種質量保證。 請務必確保如果負載在一段時間內可能會有顯著差異,主機環境可調整。
  • 基於獲利目的,可能需要計量要求。
  • 可能需要規範流量流向 Web API,並針對已耗盡配額的特定用戶端實作節流。
  • 法規需求可能會要求記錄和稽核所有要求和回應。
  • 若要確保可用性,可能需要監視裝載 Web API 的伺服器健康情況,並視需要重新啟動。

能夠將這些問題與 Web API 實作相關的技術問題分離,這非常有用。 基於這個理由,請考慮建立 外觀,以個別進程的形式執行,並將要求路由傳送至 Web API。 外觀可以提供管理作業,並將已驗證的要求轉送至 Web API。 使用外觀也可以帶來許多功能優勢,包括:

  • 做為多個 Web API 的整合點。
  • 針對使用不同技術所建置的用戶端轉換訊息和翻譯通訊協定。
  • 快取要求和回應,以減少裝載 Web API 的伺服器上負載。

測試 Web API

Web API 應該與任何其他軟體一樣徹底地進行測試。 您應該考慮建立單元測試來驗證功能。

Web API 的性質帶來了自己的額外需求,以確認其運作正確。 您應該特別注意下列層面:

  • 測試所有路由,以確認它們叫用正確的作業。 特別請注意,意外傳回 HTTP 狀態代碼 405 (不允許的方法),這表示路由與 HTTP 方法(GET、POST、PUT、DELETE)之間不符,因此可以分派至該路由。

    將 HTTP 要求傳送至不支援它們的路由,例如將 POST 要求提交至特定資源(POST 要求應該只傳送至資源集合)。 在這些情況下,唯一有效的回應 應該是 狀態代碼 405 (不允許)。

  • 確認所有路由都受到適當的保護,並受到適當的驗證和授權檢查。

    注意

    某些安全性層面,例如用戶驗證,很可能是主機環境的責任,而不是 Web API,但仍需要包含安全性測試作為部署程式的一部分。

  • 測試每個作業所執行的例外狀況處理,並確認適當且有意義的 HTTP 回應會傳回用戶端應用程式。

  • 確認要求和回應消息的格式良好。 例如,如果 HTTP POST 要求包含 x-www-form-urlencoded 格式的新資源數據,請確認對應的作業正確地剖析數據、建立資源,並傳回包含新資源詳細數據的回應,包括正確的 Location 標頭。

  • 確認回應消息中的所有連結和 URI。 例如,HTTP POST 訊息應該會傳回新建立資源的 URI。 所有HATEOAS連結都應該有效。

  • 請確定每個作業都會針對不同的輸入組合傳回正確的狀態代碼。 例如:

    • 如果查詢成功,它應該會傳回狀態代碼 200 (確定)
    • 如果找不到資源,作業應該會傳回 HTTP 狀態代碼 404 (找不到)。
    • 如果客戶端傳送成功刪除資源的要求,狀態代碼應該是 204 (沒有內容)。
    • 如果客戶端傳送建立新資源的要求,狀態代碼應該是 201(已建立)。

請留意 5xx 範圍內的非預期響應狀態代碼。 這些訊息通常是由主機伺服器報告,表示無法滿足有效的要求。

  • 測試客戶端應用程式可以指定的不同要求標頭組合,並確保 Web API 會傳回回應消息中預期的資訊。

  • 測試查詢字串。 如果作業可以採用選擇性參數(例如分頁要求),請測試不同的參數組合和順序。

  • 確認異步操作是否成功完成。 如果 Web API 支援針對傳回大型二進位物件(例如視訊或音訊)的要求進行串流處理,請確定數據流處理時不會封鎖用戶端要求。 如果 Web API 針對長時間執行的數據修改作業實作輪詢,請確認作業在繼續時正確回報其狀態。

您也應該建立並執行效能測試,以檢查 Web API 在強制下運作是否令人滿意。 您可以使用 Visual Studio Ultimate 建置 Web 效能和負載測試專案。

使用 Azure API 管理

在 Azure 上,請考慮使用 Azure API 管理 來發佈和管理 Web API。 使用此設施,您可以產生服務,做為一或多個 Web API 的外觀。 服務本身是一種可調整的 Web 服務,您可以使用 Azure 入口網站 來建立和設定。 您可以使用此服務來發佈和管理 Web API,如下所示:

  1. 將 Web API 部署至網站、Azure 雲端服務或 Azure 虛擬機。

  2. 連線 API 管理服務至 Web API。 傳送至管理 API URL 的要求會對應至 Web API 中的 URI。 相同的 API 管理服務可以將要求路由傳送至多個 Web API。 這可讓您將多個 Web API 匯總成單一管理服務。 同樣地,如果您需要限制或分割不同應用程式可用的功能,可以從多個 API 管理服務參考相同的 Web API。

    注意

    在 HTTP GET 要求回應中產生的 HATEOAS 連結中,URI 應該參考 API 管理服務的 URL,而不是裝載 Web API 的 Web 伺服器。

  3. 針對每個 Web API,指定 Web API 公開的 HTTP 作業,以及作業可作為輸入的任何選擇性參數。 您也可以設定 API 管理服務是否應該快取從 Web API 收到的回應,以優化相同數據的重複要求。 記錄每個作業可產生之 HTTP 回應的詳細數據。 此資訊可用來為開發人員產生檔,因此請務必正確且完整。

    您可以使用 Azure 入口網站 所提供的精靈手動定義作業,也可以從包含WADL或 Swagger 格式定義的檔案匯入作業。

  4. 設定 API 管理服務與裝載 Web API 之 Web 伺服器之間通訊的安全性設定。 API 管理服務目前支援使用憑證的基本身份驗證和相互驗證,以及 OAuth 2.0 用戶授權。

  5. 建立產品。 產品是出版物單位;您會將先前連線至管理服務的Web API新增至產品。 發佈產品時,Web API 可供開發人員使用。

    注意

    發佈產品之前,您也可以定義可存取產品並將使用者新增至這些群組的使用者群組。 這可讓您控制可使用 Web API 的開發人員和應用程式。 如果 Web API 必須經過核准,才能存取 Web API,開發人員必須將要求傳送給產品管理員。 系統管理員可以授與或拒絕開發人員的存取權。 如果情況有所變更,也可以封鎖現有的開發人員。

  6. 設定每個 Web API 的原則。 原則會控管是否應允許跨網域呼叫、如何驗證用戶端、是否透明地轉換 XML 與 JSON 數據格式、是否限制來自指定 IP 範圍的呼叫、使用配額,以及是否限制呼叫速率等層面。 原則可以全域套用至整個產品、產品中的單一 Web API,或 Web API 中的個別作業。

如需詳細資訊,請參閱 API 管理 檔

提示

Azure 提供 Azure 流量管理員,可讓您實作故障轉移和負載平衡,並減少裝載在不同地理位置之網站的多個實例之間的延遲。 您可以搭配 API 管理 服務使用 Azure 流量管理員;API 管理 服務可以透過 Azure 流量管理員 將要求路由傳送至網站的實例。 如需詳細資訊,請參閱流量管理員路由方法

在此結構中,如果您使用網站的自定義 DNS 名稱,您應該為每個網站設定適當的 CNAME 記錄,以指向 Azure 流量管理員 網站的 DNS 名稱。

支援客戶端開發人員

建構用戶端應用程式的開發人員通常需要有關如何存取 Web API 的資訊,以及說明 Web 服務與用戶端應用程式之間不同要求和回應的參數、數據類型、傳回型別和傳回碼的相關文件。

記錄 Web API 的 REST 作業

Azure API 管理 服務包含開發人員入口網站,描述Web API所公開的REST作業。 產品發佈後,會出現在此入口網站上。 開發人員可以使用此入口網站註冊存取權;系統管理員接著可以核准或拒絕要求。 如果開發人員獲得核准,則會獲派訂用帳戶密鑰,用來驗證他們所開發用戶端應用程式的呼叫。 此金鑰必須提供給每個 Web API 呼叫,否則將會遭到拒絕。

此入口網站也提供:

  • 產品的檔,列出其公開的作業、所需的參數,以及可傳回的不同回應。 請注意,這項資訊是使用 Microsoft Azure API 管理 服務一節,從發佈 Web API 清單中步驟 3 中提供的詳細數據產生。
  • 示範如何從數種語言叫用作業的代碼段,包括 JavaScript、C#、Java、Ruby、Python 和 PHP。
  • 開發人員主控台,可讓開發人員傳送 HTTP 要求來測試產品中的每個作業,並檢視結果。
  • 開發人員可以回報任何問題或問題的頁面。

Azure 入口網站 可讓您自定義開發人員入口網站,以變更樣式和版面配置,以符合組織的商標。

實作用戶端 SDK

建置叫用 REST 要求來存取 Web API 的用戶端應用程式需要撰寫大量的程式碼來建構每個要求並適當地格式化它、將要求傳送至裝載 Web 服務的伺服器,以及剖析回應,以找出要求成功或失敗,以及擷取傳回的任何數據。 若要隔離用戶端應用程式與這些考慮,您可以提供一個 SDK 來包裝 REST 介面,並將這些低階詳細數據抽象化在一組功能更實用的方法內。 用戶端應用程式會使用這些方法,以透明方式將呼叫轉換成 REST 要求,然後將響應轉換回方法傳回值。 這是許多服務所實作的常見技術,包括 Azure SDK。

建立用戶端 SDK 是相當大量的工作,因為它必須一致地實作,並仔細測試。 不過,此程式的大部分都可以進行機械化,而且許多廠商都提供工具,可將其中許多工作自動化。

監視 Web API

視您發佈和部署 Web API 的方式而定,您可以直接監視 Web API,或分析通過 API 管理 服務的流量來收集使用量和健康情況資訊。

直接監視 Web API

如果您已使用 ASP.NET Web API 範本實作 Web API(例如 Web API 專案或 Azure 雲端服務中的 Web 角色)和 Visual Studio 2013,您可以使用 ASP.NET Application Insights 來收集可用性、效能和使用方式數據。 Application Insights 是一個套件,可在 Web API 部署至雲端時,以透明方式追蹤和記錄有關要求和回應的資訊;安裝並設定套件之後,您就不需要修改 Web API 中的任何程式代碼即可使用它。 當您將 Web API 部署至 Azure 網站時,會檢查所有流量,並收集下列統計數據:

  • 伺服器回應時間。
  • 伺服器要求數目和每個要求的詳細數據。
  • 平均回應時間方面最慢的要求。
  • 任何失敗要求的詳細數據。
  • 由不同瀏覽器和使用者代理程式起始的會話數目。
  • 最常檢視的頁面(主要適用於 Web 應用程式,而不是 Web API)。
  • 存取 Web API 的不同使用者角色。

您可以在 Azure 入口網站 中即時檢視此數據。 您也可以建立 Web 測試來監視 Web API 的健康情況。 Web 測試會將定期要求傳送至 Web API 中的指定 URI,並擷取回應。 您可以指定成功回應的定義(例如 HTTP 狀態代碼 200),如果要求未傳回此回應,您可以安排警示傳送給系統管理員。 如有必要,如果 Web API 失敗,系統管理員可以重新啟動裝載 Web API 的伺服器。

如需詳細資訊,請參閱 Application Insights - 開始使用 ASP.NET

透過 API 管理 服務監視 Web API

如果您已使用 API 管理 服務發佈 Web API,Azure 入口網站 上的 API 管理 頁面會包含儀錶板,可讓您檢視服務的整體效能。 [分析] 頁面可讓您向下切入產品使用方式的詳細數據。 此頁面包含下列索引標籤:

  • 使用情況。 此索引標籤提供已進行的 API 呼叫數目,以及一段時間內用來處理這些呼叫的頻寬相關信息。 您可以依產品、API 和作業來篩選使用量詳細數據。
  • 健康情況。 此索引標籤可讓您檢視 API 要求的結果(傳回的 HTTP 狀態代碼)、快取原則的有效性、API 回應時間,以及服務回應時間。 同樣地,您可以依產品、API 和作業來篩選健康情況數據。
  • 活動。 此索引標籤提供每個產品、Web API 和作業成功呼叫、失敗呼叫、封鎖呼叫、平均回應時間和回應時間的文字摘要。 此頁面也會列出每個開發人員發出的呼叫數目。
  • 一目了然。 此索引標籤會顯示效能數據的摘要,包括負責進行最多 API 呼叫的開發人員,以及收到這些呼叫的產品、Web API 和作業。

您可以使用這項資訊來判斷特定 Web API 或作業是否造成瓶頸,並在必要時調整主機環境並新增更多伺服器。 您也可以確定一或多個應用程式是否使用不成比例的資源數量,並套用適當的原則來設定配額和限制通話速率。

注意

您可以變更已發佈產品的詳細數據,並立即套用變更。 例如,您可以新增或移除 Web API 中的作業,而不需要重新發布包含 Web API 的產品。

下一步