在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
注:本文是【ASP.NET Web API系列教程】的一部分,如果您是第一次看本博客文章,请先看前面的内容。 本文引自:http://www.asp.net/web-api/overview/creating-web-apis/creating-a-help-page-for-a-web-api By Mike Wasson | August 3, 2012 This tutorial shows how to create a help page for a web API, by using the ApiExplorer class. The ApiExplorer class provides descriptive information about the APIs exposed by a web API. ApiExplorer provides the raw material that you can use to create a help page. The ApiExplorer contains an ApiDescription collection, one for each API that it discovers in your project. For this purpose, an "API" is defined as the combination of HTTP method and relative URI. For example, here are some distinct APIs:
If a single controller action supports multiple HTTP methods, the ApiExplorer treats each as a distinct API. For this tutorial, we will create the help page as an MVC view, using Razor syntax to render the HTML. First, create a new project using the "Web API" project template. 图2-27. 创建项目 This template creates a project that contains a Web API controller (ValuesController.cs) and an MVC controller (HomeController.cs). Add a model class: namespace HelpDemo.Models { public class Product { public string Name { get; set; } public decimal Price { get; set; } } } Add another Web API controller: namespace HelpDemo.Controllers { using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Web.Http; using HelpDemo.Models; public class ProductsController : ApiController { public IEnumerable<Product> Get() { return new Product[0]; } public HttpResponseMessage Get(int id) { return Request.CreateResponse(HttpStatusCode.NotFound); } public void Post(Product value) { } public void Put(int id, Product value) { } } } This controller is not really functional, and you don't have to use this exact code. This purpose of this controller is just to give the ApiExplorer something to consume. Next, we'll create a view model that gets data from the ApiExplorer. We could pass the ApiExplorer object directly to the MVC view. However, encapsulating the ApiExplorer in a model gives us more flexibility to manipulate the data. namespace HelpDemo.Models { using System; using System.Collections.Generic; using System.Linq; using System.Web.Http.Controllers; using System.Web.Http.Description; public class ApiModel { IApiExplorer _explorer; public ApiModel(IApiExplorer explorer) { if (explorer == null) { throw new ArgumentNullException("explorer"); } _explorer = explorer; } public ILookup<string, ApiDescription> GetApis() { return _explorer.ApiDescriptions.ToLookup( api => api.ActionDescriptor.ControllerDescriptor.ControllerName); } } } The GetApis method converts the collection into an ILookup, grouped by controller name. That makes it easy to group the help page by controller. Open the source file for the MVC controller, HomeController.cs. Add an action to render the help page: public ActionResult Help() { var explorer = GlobalConfiguration.Configuration.Services.GetApiExplorer(); return View(new ApiModel(explorer)); } Add a view for the Help action: @model HelpDemo.Models.ApiModel @{ ViewBag.Title = "API Help"; } @section Scripts { @Scripts.Render("~/bundles/jqueryui") <script type="text/javascript"> // Apply jQuery accordion widget. // 运用jQuery的可折叠部件 $(function () { $(".accordion").accordion(); }); </script> } <div > <h2>API Help</h2> @foreach (var group in Model.GetApis()) { <h3>@group.Key</h3> <div class="accordion"> @foreach (var api in group) { <h4><a href="#">@api.HttpMethod @api.RelativePath</a></h4> <div> @foreach (var param in api.ParameterDescriptions) { <p>Parameter: <em>@param.Name</em> (@param.Source)</p> } </div> } </div> } </div> If you run the application and navigate to /home/help, it should look similar to the following: 图2-28. 帮助页面外观 Adding a Documentation Provider You can provide documentation for your APIs by implementing the IDocumentationProvider interface. Documentation strings can come from any source that you like. For this example, we will define custom attributes that can be applied to the controller. The documentation provider will grab documentation strings from the attributes. For another approach, see Yao Huang Lin's blog post Generating a Web API help page using ApiExplorer. He shows a documentation provider that pulls from XML documentation comments. Here are the custom attributes: [AttributeUsage(AttributeTargets.Method)] public class ApiDocAttribute : Attribute { public ApiDocAttribute(string doc) { Documentation = doc; } public string Documentation { get; set; } } [AttributeUsage(AttributeTargets.Method)] public class ApiParameterDocAttribute : Attribute { public ApiParameterDocAttribute(string param, string doc) { Parameter = param; Documentation = doc; } public string Parameter { get; set; } public string Documentation { get; set; } } Here is a controller method with the attributes: [ApiDoc("Gets a product by ID.")] [ApiParameterDoc("id", "The ID of the product.")] public HttpResponseMessage Get(int id) { // ... } Now add a class that implements IDocumentationProvider. public class DocProvider : IDocumentationProvider { public string GetDocumentation(HttpParameterDescriptor parameterDescriptor) { string doc = ""; var attr = parameterDescriptor.ActionDescriptor .GetCustomAttributes<ApiParameterDocAttribute>() .Where(p => p.Parameter == parameterDescriptor.ParameterName) .FirstOrDefault(); if (attr != null) { doc = attr.Documentation; } return doc; } public string GetDocumentation(HttpActionDescriptor actionDescriptor) { string doc = ""; var attr = actionDescriptor.GetCustomAttributes<ApiDocAttribute>().FirstOrDefault(); if (attr != null) { doc = attr.Documentation; } return doc; } } Open the Global.asax.cs file and add the following code to the Application_Start method: GlobalConfiguration.Configuration.Services.Replace( typeof(IDocumentationProvider), new DocProvider()); ApiExplorer automatically calls into the IDocumentationProvider interface to get documentation strings for each API. It stores them in the Documentation property of the ApiDescription and ApiParameterDescription objects. The MVC view can access them as follows: @foreach (var api in group) { <h4><a href="#">@api.HttpMethod @api.RelativePath</a></h4> <div><p>@api.Documentation</p> <ul> @foreach (var param in api.ParameterDescriptions) { <li><em>@param.Name</em> (@param.Source): @param.Documentation</li> } </ul> </div> } Now when you render the view, it shows the documentation strings: 图2-29. 显示了文档字符串的帮助页面 Providing Sample Response Bodies One thing that's missing from our help page is examples of the HTTP response body. The ApiExplorer class does not provide any direct support for this. However, it does give you the return type for each action. If the action returns a POCO, we can use this information to create an example response body, as follows:
To see how this can work, add the following code to the ApiModel class: // Example instances // 例子实例 static Product[] _products = new Product[] { new Product() { Name = "Gizmo", Price = 1.05M }, new Product() { Name = "Gizmo2", Price = 0.995M } }; // Look-up table based on data type. // 基于数据类型查询表 Dictionary<Type, object> _sampleData = new Dictionary<Type, object>() { { typeof(Product), _products[0] }, { typeof(IEnumerable<Product>), _products } }; Type GetResponseType(HttpActionDescriptor action) { return action.ReturnType; } public string GetSampleResponseBody(ApiDescription api, string mediaType) { string body = null; Type returnType = GetResponseType(api.ActionDescriptor); object o; if (returnType != null && _sampleData.TryGetValue(returnType, out o)) { var formatters = api.SupportedResponseFormatters; MediaTypeFormatter formatter = formatters.FirstOrDefault( f => f.SupportedMediaTypes.Any(m => m.MediaType == mediaType)); if (formatter != null) { var content = new ObjectContent(returnType, o, formatter); body = content.ReadAsStringAsync().Result; } } return body; } Add the following code to the MVC view: @{ string response = @Model.GetSampleResponseBody(api, "application/json"); } @if (response != null) { <p>Sample response body:</p> <p><code>@Model.GetSampleResponseBody(api, "application/json")</code></p> } Here is the resulting help page: 图2-30. 显示了示例的帮助页面 This approach is not perfect. If an action returns an HttpResponseMessage, there is no way to deduce the format of the message body from the static API description. The same is true for an action that returns loosely-structure JSON (see Anonymous and Weakly-Typed Objects). One way to work around this problem is to use the API ID to look up the response type. The API ID is a concatenation of the HTTP method and the relative URI path (for example, "GETapi/Products/{id}"). // Response types // 响应类型 static Dictionary<string, Type> _responseTypes = new Dictionary<string, Type>() { { @"GETapi/Products/{id}", typeof(Product) } }; Type GetResponseType(ApiDescription api) { Type t; if (_responseTypes.TryGetValue(api.ID, out t)) { return t; } return api.ActionDescriptor.ReturnType; } Custom attributes are another option. Excluding an API To hide an API from the ApiExplorer, add the ApiExplorerSettings attribute to the action and set IgnoreApi to true. [ApiExplorerSettings(IgnoreApi=true)] public HttpResponseMessage Get(int id) { } You can also add this attribute to the controller, to exclude the entire controller. 看完此文如果觉得有所收获,恳请给个推荐 |
请发表评论