目前,我有一个ASP.NET Web Api与ASP.NET Web应用程序并行运行。使用Yao Huang在与HelpPage NuGet包相关的博客文章中概述的方法,特别是XMLDocumentationProvider,我已经成功地记录了我的API - 几乎......
我遇到的问题是特定的API方法调用继承(并可能覆盖)基类的行为,我们称之为ApiBase
。因此,这些方法没有自己的文档,因为它们从基类继承方法,并且通常不需要ApiBase
提供的进一步解释。 对于从基类继承实现的方法,我正在尝试做什么(并且没有指示如何做)是找到一种方法来继承和显示与基类关联的关联XML注释并使用所调用的方法显示它们。
为了澄清这一点,以下是ApiBase
的示例方法:
public abstract class ApiBase
{
/// <summary>
/// Stub used as an example
/// </summary>
/// <param name="stub">Random boolean value</param>
/// <returns>The boolean value</returns>
public virtual bool returnBool(bool stub)
{
return stub;
}
/// <summary>
/// Stub #2 used as an example
/// </summary>
/// <param name="stub">Random int value</param>
/// <returns>A value less than the parameter</returns>
public virtual int returnLess(int stub)
{
return (stub - 10);
}
}
稍后,假设我们有一个继承此功能的控制器类ApiChild
:
public class ApiChild : ApiBase
{
public override int returnLess(int stub)
{
return (stub - 20);
}
}
当ApiChild
调用returnBool
或returnLess
时,我希望他们的评论可以通过XMLDocumentationProvider
从基类中获取。 ApiExplorer已经从基类成功地获取了其余信息,例如返回类型,参数等,但我不确定如何将此功能扩展到注释检索。我感谢您提供的任何帮助。我自己的想法倾向于使用反射在运行时分析方法以确定其属性的某种实现,并且如果需要,以某种方式适当地从其父级获取注释。非常感谢任何想法/指导。
供参考,以下是XMLDocumentationProvider
中使用的当前代码:
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
using System.Xml.XPath;
namespace MyProject.Areas.HelpPage
{
public interface IResponseDocumentationProvider
{
string GetResponseDocumentation(HttpActionDescriptor actionDescriptor);
}
/// <summary>
/// A custom <see cref="IDocumentationProvider"/> that reads the API documentation from an XML documentation file.
/// </summary>
public class XmlDocumentationProvider : IDocumentationProvider, IResponseDocumentationProvider
{
private XPathNavigator _documentNavigator;
private const string MethodExpression = "/doc/members/member[@name='M:{0}']";
private const string ParameterExpression = "param[@name='{0}']";
/// <summary>
/// Initializes a new instance of the <see cref="XmlDocumentationProvider"/> class.
/// </summary>
/// <param name="documentPath">The physical path to XML document.</param>
public XmlDocumentationProvider(string documentPath)
{
if (documentPath == null)
{
throw new ArgumentNullException("documentPath");
}
XPathDocument xpath = new XPathDocument(documentPath);
_documentNavigator = xpath.CreateNavigator();
}
public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor)
{
XPathNavigator methodNode = GetMethodNode(actionDescriptor);
if (methodNode != null)
{
XPathNavigator summaryNode = methodNode.SelectSingleNode("summary");
if (summaryNode != null)
{
return summaryNode.Value.Trim();
}
}
return null;
}
public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
{
ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor;
if (reflectedParameterDescriptor != null)
{
XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor);
if (methodNode != null)
{
string parameterName = reflectedParameterDescriptor.ParameterInfo.Name;
XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName));
if (parameterNode != null)
{
return parameterNode.Value.Trim();
}
}
}
return null;
}
private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor)
{
ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor;
if (reflectedActionDescriptor != null)
{
string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo));
return _documentNavigator.SelectSingleNode(selectExpression);
}
return null;
}
private static string GetMemberName(MethodInfo method)
{
string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", method.DeclaringType.FullName, method.Name);
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length != 0)
{
string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray();
name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames));
}
return name;
}
private static string GetTypeName(Type type)
{
if (type.IsGenericType)
{
// Format the generic type name to something like: Generic{System.Int32,System.String}
Type genericType = type.GetGenericTypeDefinition();
Type[] genericArguments = type.GetGenericArguments();
string typeName = genericType.FullName;
// Trim the generic parameter counts from the name
typeName = typeName.Substring(0, typeName.IndexOf('`'));
string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray();
return String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", typeName, String.Join(",", argumentTypeNames));
}
return type.FullName;
}
public virtual string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
{
XPathNavigator methodNode = GetMethodNode(actionDescriptor);
if (methodNode != null)
{
XPathNavigator returnsNode = methodNode.SelectSingleNode("returns");
if (returnsNode != null)
return returnsNode.Value.Trim();
}
return null;
}
}
}