你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
Web API 实现
精心设计的 REST 样式 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 操作
避免根据聊天操作来设计 API。 每个请求都有协议、网络和计算开销。 例如,在客户端、网络和资源服务器上执行 100 个较小的请求而不是一个更大的批处理请求会产生额外的开销。 尽可能为资源集合提供 HTTP 谓词支持,而不仅仅是单个资源。
- 对集合的 GET 请求可以同时检索多个资源。
- POST 请求可以包含多个新资源的详细信息,并将其全部添加到同一集合。
- PUT 请求可以替换集合中的整个资源集。
- DELETE 请求可以删除整个集合。
ASP.NET Web API 2 中包含的 Open Data Protocol (OData) 支持提供了批处理请求的功能。 客户端应用程序可以打包多个 Web API 请求并通过单个 HTTP 请求将其发送给服务器,然后接收包含每个请求的答复的单个 HTTP 响应。 有关详细信息,请参阅在 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 样式导航和资源发现的链接
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 网站上的 Status code definitions(状态代码定义)页中找到 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 服务器在错误条件到达 Web API 之前,自行捕获错误条件。 例如,如果为网站配置了身份验证,但用户无法提供正确的身份验证信息,则 Web 服务器应以状态代码 401(未经授权)进行响应。 客户端经过身份验证后,代码可以执行自己的检查来验证客户端是否应能够访问所请求的资源。 如果此授权失败,则应返回状态代码 403(禁止访问)。
一致地处理异常,并记录有关错误的信息
若要以一致方式处理异常,请考虑在整个 Web API 中实现全局错误处理策略。 还应将整合捕获每个异常的完整详细信息的错误日志记录;只要客户端无法通过 Web 访问它,此错误日志就会包含详细的信息。
区分客户端错误和服务器端错误
HTTP 协议可区分因客户端应用程序发生的错误(HTTP 4xx 状态代码)和因服务器上的事故导致的错误(HTTP 5xx 状态代码)。 请确保在任何错误响应消息中遵守此约定。
优化客户端数据访问
例如,在分布式环境(例如,涉及 Web 服务器和客户端应用程序)中,主要问题源之一是网络。 这可能表现为值得注意的瓶颈问题,尤其当客户端应用程序频繁地将发送请求或接收数据时。 因此,目标应是将网络间流动的通信量降到最低。 在实现检索和维护数据的代码时,请考虑以下几点:
支持客户端缓存
HTTP 1.1 协议支持在客户端和中间服务器中缓存,请求通过这些客户端和服务器使用 Cache-Control 标头进行路由。 当客户端应用程序向 Web API 发送 HTTP GET 请求时,响应可以包含 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 标头可以指定 public(而不是 private),在这种情况下,数据可以存储在共享缓存中;它也可以指定 no-store,在这种情况下,数据不能由客户端缓存。 下面的代码示例显示如何在响应消息中构造 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 标头中的 max-age 值只是指南,并不保证相应的数据在指定的时间内不会更改。 Web API 应将 max-age 设置为适当的值,具体取决于数据的预期波动性。 此期间到期后,客户端应放弃缓存中的对象。
注意
如前所述,大多数现代 Web 浏览器通过向请求添加相应的 Cache-Control 标头并检查结果的标头来支持客户端缓存。 但是,某些较旧的浏览器将不缓存从包含查询字符串的 URL 返回的值。 这通常不是基于此处讨论的协议实现自己的缓存管理策略的自定义客户端应用程序的问题。
某些较旧的代理显示相同的行为并可能不会基于包含查询字符串的 URL 缓存请求。 这可能是通过此类代理连接到 Web 服务器的自定义客户端应用程序的问题。
提供 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 请求:
客户端构造 GET 请求,该请求包含 If-None-Match HTTP 标头中引用的资源的当前缓存版本的 ETag:
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 应返回状态代码为 404(未找到)的 HTTP 响应。
客户端使用状态代码来维护缓存。 如果数据尚未更改(状态代码 304),则对象可保持缓存,并且客户端应用程序应继续使用此版本的对象。 如果数据已更改(状态代码 200),则应放弃缓存的对象,并插入新对象。 如果数据不再可用(状态代码 404),则应从缓存中删除该对象。
注意
如果响应标头包含的 Cache-Control 标头为 no-store,则应始终从缓存中删除对象,而不考虑 HTTP 状态代码。
以下代码显示了已扩展为支持 If-None-Match 标头的 FindOrderByID
方法。 请注意,如果省略 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 应返回包含空的消息正文和状态代码 412(不满足前提条件)的 HTTP 响应。
如果要更新的资源不再存在,则 Web API 应返回状态代码为 404(未找到)的 HTTP 响应。
客户端使用状态代码和响应标头来维护缓存。 如果数据已更新(状态代码 204),则对象可保持缓存(只要 Cache-Control 标头未指定 no-store),但应更新 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 标头。
处理大型请求和响应
可能会有这样的情况:客户端应用程序需要发出用于发送或接收大小可能为几兆字节(或更大)的数据的请求。 等待传输如此大量的数据可能会导致客户端应用程序停止响应。 在需要处理包含大量数据的请求时,请考虑以下几点:
优化涉及大型对象的请求和响应
一些资源可能是大型对象或包含大量字段,例如图形图像或其他类型的二进制数据。 Web API 应支持流式处理以对这些资源的上传和下载进行优化。
HTTP 协议提供了分块传输编码机制,用于将大型数据对象流式传输回客户端。 当客户端为大型对象发送 HTTP GET 请求时,Web API 可以通过 HTTP 连接在段落区块中发送回答复。 最初答复中数据的长度可能未知(可能会生成它),因此托管 Web API 的服务器应发送包含每个区块的响应消息,这些区块指定 Transfer-Encoding: Chunked
标头而不是 Content-Length 标头。 客户端应用程序可以依次接收每个区块以组合成完整响应。 在数据传输完成后,服务器将发送回最后一个区块(大小为零)。
可以想象单个请求生成使用大量资源的大型对象。 如果在流式处理过程中,Web API 确定请求中的数据量超过了可接受的范围,它可以中止操作并返回具有状态代码 413(请求实体太大)的响应消息。
可以使用 HTTP 压缩将通过网络传输的大型对象的大小降到最低。 此方法可帮助减少网络流量和关联的网络延迟,但代价是需要在客户端和托管 Web API 的服务器上进行额外的处理。 例如,需要接收压缩数据的客户端应用程序可以提供 Accept-Encoding: gzip
请求头(还可以指定其他数据压缩算法)。 如果服务器支持压缩,则应以消息正文中以 gzip 格式存储的内容以及 Content-Encoding: gzip
响应头进行响应。
可以将编码压缩和流式处理结合使用;在流式处理数据之前先压缩它,并在消息标头中指定 gzip 内容编码和 chunked 传输编码。 另请注意,某些 Web 服务器(如 Internet Information Server)可以配置为自动压缩 HTTP 响应,而不管 Web API 是否压缩数据。
为不支持异步操作的客户端实现部分响应
作为异步流式处理的替代方法,客户端应用程序可以区块方式显式请求大型对象的数据,称为部分响应。 客户端应用程序发送 HTTP HEAD 请求以获取有关对象的信息。 如果 Web API 支持部分响应,则应以包含 Accept-Ranges
标头和 Content-Length
标头(指示该对象的总大小),但消息正文应为空的响应消息响应 HEAD 请求。 客户端应用程序可以使用此信息来构造一系列要接收的在指定字节范围的 GET 请求。 Web API 应返回包含以下内容的响应消息:HTTP 状态 206(部分内容)、指定响应消息正文中包含的实际数据量的 Content-Length 标头,以及指示此数据表示对象的哪一部分(例如 4000
到 8000
个字节)的 Content-Range 标头。
HTTP HEAD 请求和部分响应会在 API 设计中详细介绍。
避免在客户端应用程序中发送不必要的“100-Continue”状态消息
将要发送大量数据到服务器的客户端应用程序可能会先确定服务器是否实际可以接受该请求。 在发送数据之前,客户端应用程序可以提交一个 HTTP 请求,其中包含 Expect: 100-Continue 标头、Content-Length 标头(指示数据的大小),但消息正文为空。 如果服务器可以处理该请求,则应以指定 HTTP 状态 100(继续)的消息进行响应。 然后,客户端应用程序可以继续操作并发送包含消息体中的数据的完整请求。
如果使用 Internet 信息服务 (IIS) 来托管服务,则 HTTP.sys 驱动程序会自动检测并处理 Expect: 100-Continue 标头,然后再将请求传递到 Web 应用程序。 这意味着你很可能在应用程序代码中看不到这些标头,可以假设 IIS 已筛选掉任何它认为不适合或太大的消息。
如果使用.NET Framework 生成客户端应用程序,则默认情况下所有 POST 和 PUT 消息都将先发送包含 Expect: 100-Continue 标头的消息。 与服务器端一样,由 .NET Framework 透明地处理该过程。 但是,此过程的结果是,每个 POST 和 PUT 请求会导致对服务器进行 2 次往返,即使是小请求,也是如此。 如果应用程序不发送包含大量数据的请求,则可以通过在客户端应用程序中使用 ServicePointManager
类创建 ServicePoint
对象来禁用此功能。 ServicePoint
对象将基于标识服务器上的资源的 URI 的方案和主机片段处理客户端与服务器连接的创建。 然后,可以将 ServicePoint
对象的 Expect100Continue
属性设置为 false。 客户端通过与 ServicePoint
对象的方案和主机片段匹配的 URI 发出的所有后续 POST 和 PUT 请求在发送时会不包含 Expect: 100-Continue 标头。 下面的代码演示如何配置 ServicePoint
对象,以便将所有请求都发送到方案为 http
且主机为 www.contoso.com
的 URI。
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 应支持这样的查询字符串:使客户端应用程序可以细化请求或在更易于管理的离散块(或页)中提取数据。 以下代码显示 Orders
控制器中的 GetAllOrders
方法。 此方法检索订单的详细信息。 如果此方法不受约束,可以想像它可能返回大量数据。 limit
和 offset
参数旨在将数据量减少到较小子集,在本示例中默认情况下为前 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 个订单。
提示
请避免让客户端应用程序指定的查询字符串导致 URI 超过 2000 个字符。 许多 Web 客户端和服务器无法处理这么长的 URI。
保持响应能力、可伸缩性和可用性
同一 Web API 可能由世界任何地方运行的许多客户端应用程序使用。 请务必确保将 Web API 实现为在重负载下保持响应能力、可扩展以支持高度变化的工作负荷,并保证执行关键业务操作的客户端的可用性。 确定如何满足这些要求时,请考虑以下几点:
为长时间运行的请求提供异步支持
可能需要很长的时间来处理的请求应在执行时确保不会阻塞提交请求的客户端。 Web API 可以执行一些初始检查来验证请求,启动单独的任务来执行工作,并返回包含 HTTP 代码 202(已接受)的响应消息。 任务可作为 Web API 处理的一部分异步运行,或可将其卸载为后台任务。
Web API 还应提供一种向客户端应用程序返回处理结果的机制。 可以通过以下两种方案实现此目的:为客户端应用程序提供轮询机制以定期查询处理是否已完成并获取结果,或者使 Web API 可以在操作完成时发送通知。
可以使用以下方法,通过提供充当虚拟资源的轮询 URI 来实现简单的轮询机制:
- 客户端应用程序将初始请求发送到 Web API。
- Web API 将有关该请求的信息存储在 Azure 表存储或 Microsoft Azure 缓存中保存的表中,并可能以全局唯一标识符 (GUID) 形式为此条目生成唯一密钥。 或者,也可以通过 Azure 服务总线发送包含请求和唯一密钥信息的消息。
- Web API 以单独任务的形式或使用 Hangfire 之类的库启动处理。 Web API 在表中将该任务的状态记录为“正在运行”。
- 如果你使用 Azure 服务总线,则消息处理将与 API 分开进行(可能会使用 Azure Functions 或 AKS)。
- Web API 返回带有 HTTP 状态代码 202(已接受)的响应消息,以及包含生成的唯一密钥的 URI - 类似于 /polling/{guid}。
- 完成该任务后,Web API 将结果存储在表中,并将该任务的状态设置为完成。 请注意,如果该任务失败,Web API 还可能存储有关失败的信息并将状态设置为“失败”。
- 考虑应用重试技术来解决可能的暂时性故障。
- 该任务运行时,客户端可以继续执行其自己的处理。 它可以定期向先前收到的 URI 发送请求。
- URI 中的 Web API 在表中查询相应任务的状态并返回带有 HTTP 状态代码 200(正常)的响应消息,该消息包含以下状态:正在运行、完成或失败。 如果该任务已完成或失败,则响应消息还可以包括处理的结果或获得的有关失败原因的任何信息。
- 如果长时间运行的进程存在其他中间状态,最好使用支持 saga 模式的库,例如 NServiceBus 或 MassTransit。
用于实现通知的选项包括:
- 使用通知中心将异步响应推送到客户端应用程序。 关于更多信息,请参阅使用 Azure 通知中心向特定用户发送通知。
- 使用 Comet 模型来保持客户端与托管 Web API 的服务器之间的永久网络连接,并使用此连接将消息从服务器推送回客户端。 MSDN 杂志文章 Building a Simple Comet Application in the Microsoft .NET Framework(在 Microsoft .NET Framework 中构建简单的 Comet 应用程序)介绍了一个示例解决方案。
- 使用 SignalR 通过永久网络连接将 Web 服务器中的实时数据推送到客户端。 SignalR 可作为 NuGet 程序包用于 ASP.NET Web 应用程序。 可以在 ASP.NET SignalR 网站上找到详细信息。
确保每个请求都是无状态的
每个请求应视为原子的。 客户端应用程序发出的一个请求应与同一客户端提交的任何后续请求之间没有任何依赖关系。 此方法可帮助你实现伸缩性;可以在多个服务器上部署 Web 服务的实例。 客户端请求可以定向到其中任一实例,并且结果应始终相同。 由于类似的原因,它还提高了可用性;如果某个 Web 服务器失败,可以将请求路由到其他实例(通过使用 Azure 流量管理器),同时重新启动该服务器,对客户端应用程序没有任何不良影响。
跟踪客户端并实现限制以降低 DoS 攻击的可能性
如果特定客户端在给定的时间段内发出了大量请求,则它可能会独占服务并影响其他客户端的性能。 若要缓解此问题,Web API 可以通过跟踪所有传入请求的 IP 地址或通过记录每次经过身份验证的访问来监视客户端应用程序的调用。 可以使用此信息来限制资源访问。 如果客户端超出定义的限制,Web API 可以返回包含状态 503(服务不可用)的响应消息并在其中包括 Retry-After 标头以指定客户端可以发送下一个请求而不会被拒绝的时间。 此策略可帮助降低停止系统的一组客户端发起拒绝服务 (DoS) 攻击的可能性。
小心管理永久 HTTP 连接
HTTP 协议支持永久 HTTP 连接(在这些连接可用时)。 HTTP 1.0 规范已添加 Connection:Keep-Alive 标头以允许客户端应用程序向服务器表明它可能使用同一连接发送后续请求而不是打开新连接。 如果客户端在主机定义的时间段内未重用连接,则该连接会自动关闭。 此行为在 Azure 服务使用的 HTTP 1.1 中是默认设置,因此无需在消息中包括 Keep-Alive 标头。
让连接保持打开状态可以减少延迟和网络拥塞,从而有助于提高响应能力,但让不必要的连接保持打开状态的时间大于所需时间可能会不利于可扩展性,从而限制了其他并发客户端进行连接的能力。 如果客户端应用程序运行在移动设备上,则还会影响电池使用寿命;如果应用程序只偶尔向服务器发出请求,则维持连接的打开状态可能会导致电池更快耗尽。 若要确保不使用 HTTP 1.1 建立永久连接,客户端可以在消息中包括 Connection:Close 标头以覆盖默认行为。 同样,如果服务器正在处理大量客户端请求,它可以在响应消息中包括 Connection:Close 标头,这会关闭连接并节省服务器资源。
注意
永久 HTTP 连接是纯粹的可选功能,用于减少与反复建立通信通道关联的网络开销。 Web API 和客户端应用程序都不应依赖于可用的永久 HTTP 连接。 不要使用永久 HTTP 连接实现 Comet 样式通知系统,而是应利用 TCP 层的套接字(或可用的 Web 套接字)。 最后,请注意:如果客户端应用程序通过代理与服务器通信,则 Keep-Alive 标头的作用有限;只有与客户端和代理的连接将是持久的。
发布和管理 Web API
要使 Web API 可供客户端应用程序使用,Web API 必须部署到主机环境中。 此环境通常是 Web 服务器,尽管它可能是某种其他类型的主机进程。 发布 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,如下所示:
将 Web API 部署到网站、Azure 云服务或 Azure 虚拟机。
将 API 管理服务连接到 Web API。 发送到管理 API 的 URL 的请求将映射到 Web API 中的 URI。 同一 API 管理服务可以将请求路由到多个 Web API。 这样便可以将多个 Web API 聚合为单个管理服务。 同样,如果需要限制或分隔可用于不同应用程序的功能,则可以从多个 API 管理服务引用同一 Web API。
注意
作为 HTTP GET 请求的响应的一部分生成的 HATEOAS 链接中的 URI 应引用 API 管理服务(而不是托管 Web API 的 Web 服务器)的 URL。
对于每个 Web API,指定该 Web API 公开的 HTTP 操作以及操作可以获取为输入的任何可选参数。 还可以配置 API 管理服务是否应缓存从 Web API 接收的响应以优化对相同数据的重复请求。 记录每个操作可以生成的 HTTP 响应的详细信息。 此信息用于为开发人员生成文档,因此它应准确且完整。
可以使用 Azure 门户提供的向导手动定义操作,也可以从包含 WADL 或 Swagger 格式的定义的文件中导入操作。
为 API 管理服务与托管 Web API 的 Web 服务器之间的通信配置安全设置。 API 管理服务目前支持使用证书和 Open Authorization (OAuth) 2.0 用户授权的基本身份验证和相互身份验证。
创建产品。 产品是发布的单元;可将先前连接到管理服务的 Web API 添加到产品。 发布产品后,该 Web API 便可供开发人员使用了。
注意
在发布产品之前,还可以定义可以访问该产品的用户组,并将用户添加到这些组。 这让可以控制使用该 Web API 的开发人员的应用程序的访问。 如果 Web API 需要批准,则在能够访问它之前,开发人员必须向产品管理员发送请求。 管理员可以授予或拒绝开发人员的访问权限。 如果情况发生变化,也可以阻止现有开发人员。
为每个 Web API 配置策略。 策略可以控制以下方面:是否应允许跨域调用、如何对客户端进行身份验证、是否要在 XML 和 JSON 数据格式之间透明地进行转换、是否要限制从给定 IP 范围发起的调用、使用配额,以及是否要限制调用率等。 策略可以对整个产品全局应用、对产品中的单个 Web API 应用,或者对 Web API 中的单个操作应用。
有关详细信息,请参阅API 管理文档。
提示
Azure 提供了使用 Azure 流量管理器,使用它可以实现故障转移和负载均衡,并可以减少在不同地理位置托管的多个网站实例之间的延迟。 可以将 Azure 流量管理器与 API 管理服务结合使用;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 项目还是作为 Azure 云服务中的 Web 角色)和 Visual Studio 2013 实现 Web API,则可以通过使用 ASP.NET Application Insights 收集可用性、性能和使用情况数据。 Application Insights 是在 Web API 部署到云中后透明地跟踪和记录有关请求和响应的信息的程序包;安装并配置该包后,无需修改 Web API 中的任何代码即可使用它。 将 Web API 部署到 Azure 网站时,会检查所有通信并收集以下统计信息:
- 服务器响应时间。
- 服务器请求数和每个请求的详细信息。
- 就平均响应时间而言,速度最慢的前几个请求。
- 任何失败的请求的详细信息。
- 由不同浏览器和用户代理启动的会话数。
- 最经常查看的网页(主要适用于 Web 应用程序而不是 Web API)。
- 访问 Web API 的不同用户角色。
可以在 Azure 门户实时查看此数据。 还可以创建监视 Web API 健康状况的 Web 测试。 Web 测试将定期请求发送到 Web API 中指定的 URI,并捕获响应。 可以指定成功响应的定义(如 HTTP 状态代码 200),如果请求未返回此响应,可以安排向管理员发送警报。 如有必要,管理员可以重新启动托管 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 的产品。
后续步骤
- ASP.NET Web API OData 包含有关如何使用 ASP.NET 实现 OData Web API 的示例和更多信息。
- Introducing batch support in Web API and Web API OData(Web API 和 Web API OData 中的批处理支持简介)介绍了如何使用 OData 在 Web API 中实现批处理操作。
- Jonathan Oliver 博客上的幂等性模式概述了幂等性以及它如何与数据管理操作相关。
- W3C 网站上的 Status Code Definitions(状态代码定义)包含 HTTP 状态代码及其说明的完整列表。
- 使用 WebJobs 运行后台任务提供了有关如何使用 WebJobs 执行后台操作的信息和示例。
- Azure 通知中心通知用户介绍了如何使用 Azure 通知中心将异步响应推送到客户端应用程序。
- API 管理介绍了如何发布可对 Web API 进行受控安全访问的产品。
- Azure API 管理 REST API 参考介绍了如何使用 API 管理 REST API 生成自定义管理应用程序。
- 流量管理器路由方法概述了如何使用 Azure 流量管理器对托管 Web API 的多个网站实例上的请求进行负载均衡操作。
- Application Insights - ASP.NET 入门详细介绍了如何在 ASP.NET Web API 项目中安装和配置 Application Insights。