【ASP.NET Web API教程】2.4 創建Web API的幫助頁面


注:本文是【ASP.NET Web API系列教程】的一部分,如果您是第一次看本博客文章,請先看前面的內容。

2.4 Creating a Help Page for a Web API
2.4 創建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
作者:Mike Wasson | 日期:2012-8-3

This tutorial shows how to create a help page for a web API, by using the ApiExplorer class.
本教程展示如何運用ApiExplorer類為Web API創建幫助頁面。

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:
ApiExplorer類對Web API所暴露的每個API提供了描述信息。(因此,)ApiExplorer提供了可以用來創建幫助頁面的原始材料。ApiExplorer含有一個ApiDescription集合,一個在你項目中發現的每個API的集合。為此,所謂一個“API”的定義是一個HTTP方法與相關URI的結合。例如,以下是一些不同的API:

  • GET /api/Products(HTTP方法是GET,URI是/api/Products,兩者的結合便是一個API。下同 — 譯者注)
  • GET /api/Products/{id}
  • POST /api/Products

If a single controller action supports multiple HTTP methods, the ApiExplorer treats each as a distinct API.
如果一個單一的控制器動作支持多個HTTP方法,ApiExplorer會把每一個都作為不同的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.
對於本教程,我們將把幫助頁面創建成一個MVC視圖,用Razor語法來渲染HTML。首先用“Web API”項目模板創建一個新項目(如圖2-27)。

WebAPI2-27

圖2-27. 創建項目

This template creates a project that contains a Web API controller (ValuesController.cs) and an MVC controller (HomeController.cs).
這個模板會創建含有一個Web API控制器(ValuesController.cs)和一個MVC控制器(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:
添加另一個Web API控制器:

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.
這個控制器並不是真正功能性的,而且你不必精確使用這些代碼。這個控制器的目的只是為ApiExplorer提供一些可供使用的東西。(這里意在說明,在一個實際應用程序中,可能需要一個類似於這樣的控制器,但其內容(代碼)需酌情而定 — 譯者注)

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.
下一步,我們將創建一個從ApiExplorer獲取數據的視圖模型,然后可以把ApiExplorer對象直接傳遞給MVC視圖。然而,把ApiExplorer封裝在一個模型中,可以讓我們能夠更靈活性地維護數據。

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.
GetApis方法將集合轉換成一個按控制器名稱進行分組的ILookup。這樣便於按控制器對幫助頁面進行分組。

Open the source file for the MVC controller, HomeController.cs. Add an action to render the help page:
打開MVC控制器源文件HomeController.cs。添加一個渲染幫助頁面的動作:

public ActionResult Help() 
{ 
    var explorer = GlobalConfiguration.Configuration.Services.GetApiExplorer(); 
    return View(new ApiModel(explorer)); 
}

Add a view for the Help action:
為這個Help動作添加一個視圖:

@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 id="body" class="content-wrapper"> 
<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:
如果運行這個應用程序,並導航到/home/help,頁面看上去與下圖類似(見圖2-28):

WebAPI2-28

圖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.
通過實現IDocumentationProvider接口,你可以提供API的文檔。文檔字符串(用來作為文檔內容的字符串 — 譯者注)可以是你所喜歡的任何資源(這些資源當然是關於一個API的東西,比如,該API的作用或含義的描述,有哪些參數,這些參數的類型及其作用等等 — 譯者注)。對於本例,我們將定義能夠運用於控制器的一些自定義注解屬性。文檔提供器將根據這些注解屬性捕獲文檔字符串(根據注解屬性獲取關於API的有關內容,並把這些內容送入文檔字符串中,以便在幫助頁面中顯示出來 — 譯者注)。

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.
另一種辦法參見Yao Huang Lin的博客文章“Generating a Web API help page using ApiExplorer(用ApiExplorer生成一個Web API幫助頁面)”。他演示了從XML文檔注釋進行采集的一個文檔提供器。

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.
現在,添加一個實現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:
打開Global.asax.cs文件,並將以下代碼添加到Application_Start方法:

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:
ApiExplorer會自動調用IDocumentationProvider接口,以獲取每個API的文檔字符串。把它們存儲到ApiDescriptionApiParameterDescription對象的Documentation屬性。MVC視圖像下面這樣訪問它們:

@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):

WebAPI2-29

圖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:
上述幫助頁面缺少的一項內容是HTTP響應體的示例。ApiExplorer類對此未提供任何直接支持。然而,它可以為你提供每個動作的返回類型。如果動作返回的是一個POCO,我們可以運用這個信息來創建一個例子響應體,步驟如下:

    1.
  1. Look up an example instance of the POCO.
    查找一個POCO例子實例。
  2. 2.
  3. Use a media-type formatter to serialize the example instance.
    使用一個媒體類型格式化器序列化此例子實例。
  4. 3.
  5. Display the result as a string.
    將結果以字符串顯示出來。

To see how this can work, add the following code to the ApiModel class:
要明白如何做,添加以下代碼到ApiModel類:

// 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:
將以下代碼添加到MVC視圖:

    @{  
        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):

WebAPI2-30

圖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).
這種辦法不太完美。如果一個動作返回HttpResponseMessage,則沒有辦法根據靜態API描述來推斷該消息體的格式。同樣,返回松散結構JSON的動作也是這樣(參見Anonymous and Weakly-Typed Objects(匿名類型與弱類型對象) — 本系列教程的第6章)。

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}").
解決這一問題的一種辦法是使用API ID來查找響應體類型。API ID是HTTP方法與相對URI路徑的串聯(例如,“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
排除一個API

To hide an API from the ApiExplorer, add the ApiExplorerSettings attribute to the action and set IgnoreApi to true.
為了從ApiExplorer中隱藏一個API,將ApiExplorerSettings注解屬性添加到該動作,並將IgnoreApi設置為true:

[ApiExplorerSettings(IgnoreApi=true)] 
public HttpResponseMessage Get(int id) 
{ 
}

You can also add this attribute to the controller, to exclude the entire controller.
你也可以把這個注解屬性添加到控制器,以排除整個控制器(的所有API)。

看完此文如果覺得有所收獲,懇請給個推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM