Fork me on GitHub

ASP.NET Web API RC版本新特性:Web API的帮助文档制作

InfoQ上有一篇文章是 使用IAPIExplorer列举ASP.NET Web API,文章针对的版本是ASP.NET Web API Beta版本写,IAPIExplorer已经包含在RC版本里了,而且发现ASP.NET Web API的HttpConfiguration 的RC版本比Beta版本多了一个属性:

public DefaultServices Services { get; internal set; }

Debug进去可以看到默认有18个服务,IAPIExplorer 就是其中之一:

image

使用该API可以完成的工作有:

  • 产生文档
  • 创建机器可读的元数据
  • 创建一个测试客户端

微软员工Yao发表了两篇文章(ASP.NET Web API: Introducing IApiExplorer/ApiExplorerASP.NET Web API: Generating a Web API help page using ApiExplorer)分别用于展示如何使用API打印Web API列表以及如何创建帮助文档。

所以我们创建帮助文档主要就是参照这篇文章就可以了ASP.NET Web API: Generating a Web API help page using ApiExplorer;作者在文章里面已经实现了一个IDocumentationProvider,它是通过代码的注释XML文档去生成,这是一个非常好的实现了:

public class XmlCommentDocumentationProvider : IDocumentationProvider 
    {
        XPathNavigator _documentNavigator;
        private const string _methodExpression = "/doc/members/member[@name='M:{0}']";
        private static Regex nullableTypeNameRegex = new Regex(@"(.*\.Nullable)" + Regex.Escape("`1[[") + "([^,]*),.*");
        public XmlCommentDocumentationProvider(string documentPath)
        {
            XPathDocument xpath = new XPathDocument(documentPath);
            _documentNavigator = xpath.CreateNavigator();
        } 
        public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor) 
    {
        ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor;
            if (reflectedParameterDescriptor != null)
            { 
                XPathNavigator memberNode = GetMemberNode(reflectedParameterDescriptor.ActionDescriptor);
                if (memberNode != null)
                { 
                    string parameterName = reflectedParameterDescriptor.ParameterInfo.Name;
                    XPathNavigator parameterNode = memberNode.SelectSingleNode(string.Format("param[@name='{0}']", parameterName));
                    if (parameterNode != null)
                    { 
                        return parameterNode.Value.Trim();
                    } 
                } 
            }
            return "No Documentation Found.";
        } 
        public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor)
        { 
            XPathNavigator memberNode = GetMemberNode(actionDescriptor);
            if (memberNode != null)
            { 
                XPathNavigator summaryNode = memberNode.SelectSingleNode("summary");
                if (summaryNode != null)
                { 
                    return summaryNode.Value.Trim();
                } 
            } 
            return "No Documentation Found.";
        } 
        private XPathNavigator GetMemberNode(HttpActionDescriptor actionDescriptor)
        {
            ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor;
            if (reflectedActionDescriptor != null)
            { 
                string selectExpression = string.Format(_methodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo));
                XPathNavigator node = _documentNavigator.SelectSingleNode(selectExpression);
                if (node != null)
                {
                    return node;
                } 
            } 
            return null;
        } 
        private static string GetMemberName(MethodInfo method)
        {
            string name = string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name); 
            var parameters = method.GetParameters();
            if (parameters.Length != 0) 
            { 
                string[] parameterTypeNames = parameters.Select(param => ProcessTypeName(param.ParameterType.FullName)).ToArray();
                name += string.Format("({0})", string.Join(",", parameterTypeNames));
            }
            return name;
        } 
        private static string ProcessTypeName(string typeName) 
        {
            //handle nullable
            var result = nullableTypeNameRegex.Match(typeName);
            if (result.Success)
            {
                return string.Format("{0}{{{1}}}", result.Groups[1].Value, result.Groups[2].Value);
            } 
            return typeName;
        } 
    }

把XML 文档的位置在Application_Start 中将IDocumentationProvider服务替换为我们的自定义服务。

var config = GlobalConfiguration.Configuration;
config.Services.Replace(typeof(IDocumentationProvider), new XmlCommentDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/MvcApplication2.XML")));

有个ApiExplorerSettingsAttribute 可以控制API是否在帮助页面上出现:

/// <summary>
/// DELETE api/values/5
/// </summary>
/// <param name="id"></param> 
[ApiExplorerSettings(IgnoreApi = true)]
public void Delete(int id)
{
}

还可以控制整个ApiController 的文档是否发布出来,只需要通过在类级别应用ApiExplorerSettingsAttribute

 [ApiExplorerSettings(IgnoreApi = true)]
 public class MySpecialController : ApiController

通过IAPIExplorer 这个API我们可以为我们的Web API 做出很棒的文档了,而且这个接口的设计也是可扩展的。可以制作Web页面也可以制作一个HelpController来做,推荐封装成一个HelpController,这样就可以适应WebAPi的Web Host或者是Self Host了。 

Part 1: Basic Help Page customizations

Changing the help page URI

Providing API documentations

Customizing the view

Part 2: Providing custom samples on the Help Page

Setting custom sample objects

Setting the samples when the action returns an HttpResponseMessage

Part 3: Advanced Help Page customizations

Adding additional information to the HelpPageApiModel

Creating new sample display templates

Making ASP.NET Web API Help Page work on self-hosted services

Extending Web API Help Page with information from attributes

Using curl to POST request into ASP.NET Web Api service

posted @ 2012-06-02 23:18 张善友 阅读(...) 评论(...) 编辑 收藏