Last active
July 30, 2019 09:59
-
-
Save dinowang/54b1c8e8ff86559f3c10 to your computer and use it in GitHub Desktop.
ASP.NET Web API Help Pages 目前還不支援處理主要專案 (通常是 web 專案) 以外的 XML 註解檔 , 所以若專案中有獨立 DTO 專案的設計 , Help Pages 無法順利呈現出內容於說明文件上 , 這裡參考了 StackOverflow 討論串 ( http://goo.gl/Jb1Un3 ) 的替代方案以解決燃眉之急 ; 另外 , Web API Team 也已經接受了開發者的建議 , 準備提供多個 XML Documentation 的解決方案 , 參考:http://aspnetwebstack.codeplex.com/workitem/1720
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MultipleXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider | |
{ | |
// 多個 XPathNavigator | |
private IList<XPathNavigator> _documentNavigators = new List<XPathNavigator>(); | |
private const string TypeExpression = "/doc/members/member[@name='T:{0}']"; | |
private const string MethodExpression = "/doc/members/member[@name='M:{0}']"; | |
private const string PropertyExpression = "/doc/members/member[@name='P:{0}']"; | |
private const string FieldExpression = "/doc/members/member[@name='F:{0}']"; | |
private const string ParameterExpression = "param[@name='{0}']"; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="XmlDocumentationProvider"/> class. | |
/// </summary> | |
/// <param name="documentPaths">The physical path to XML document.</param> | |
public MultipleXmlDocumentationProvider(params string[] documentPaths) | |
{ | |
if (documentPaths == null) | |
{ | |
throw new ArgumentNullException("documentPaths"); | |
} | |
// 建構函式採用可變數目引數 | |
foreach (var documentPath in documentPaths) | |
{ | |
var filePath = Path.GetDirectoryName(documentPath); | |
var fileSpec = Path.GetFileName(documentPath); | |
// 使用 wildcard 方式,如:"Mvc.*.xml" 以取得指涉檔案 | |
foreach (var file in Directory.GetFiles(filePath, fileSpec)) | |
{ | |
XPathDocument xpath = new XPathDocument(file); | |
_documentNavigators.Add(xpath.CreateNavigator()); | |
} | |
} | |
} | |
private XPathNavigator SelectSingleNode(string selectExpression) | |
{ | |
foreach (var navigator in _documentNavigators) | |
{ | |
var propertyNode = navigator.SelectSingleNode(selectExpression); | |
if (propertyNode != null) | |
{ | |
return propertyNode; | |
} | |
} | |
return null; | |
} | |
public string GetDocumentation(HttpControllerDescriptor controllerDescriptor) | |
{ | |
XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType); | |
return GetTagValue(typeNode, "summary"); | |
} | |
public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor) | |
{ | |
XPathNavigator methodNode = GetMethodNode(actionDescriptor); | |
return GetTagValue(methodNode, "summary"); | |
} | |
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; | |
} | |
public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor) | |
{ | |
XPathNavigator methodNode = GetMethodNode(actionDescriptor); | |
return GetTagValue(methodNode, "returns"); | |
} | |
public string GetDocumentation(MemberInfo member) | |
{ | |
string memberName = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(member.DeclaringType), member.Name); | |
string expression = member.MemberType == MemberTypes.Field ? FieldExpression : PropertyExpression; | |
string selectExpression = String.Format(CultureInfo.InvariantCulture, expression, memberName); | |
XPathNavigator propertyNode = SelectSingleNode(selectExpression); | |
return GetTagValue(propertyNode, "summary"); | |
} | |
public string GetDocumentation(Type type) | |
{ | |
XPathNavigator typeNode = GetTypeNode(type); | |
return GetTagValue(typeNode, "summary"); | |
} | |
private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor) | |
{ | |
ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor; | |
if (reflectedActionDescriptor != null) | |
{ | |
string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)); | |
return SelectSingleNode(selectExpression); | |
} | |
return null; | |
} | |
private static string GetMemberName(MethodInfo method) | |
{ | |
string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetTypeName(method.DeclaringType), 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 GetTagValue(XPathNavigator parentNode, string tagName) | |
{ | |
if (parentNode != null) | |
{ | |
XPathNavigator node = parentNode.SelectSingleNode(tagName); | |
if (node != null) | |
{ | |
return node.Value.Trim(); | |
} | |
} | |
return null; | |
} | |
private XPathNavigator GetTypeNode(Type type) | |
{ | |
string controllerTypeName = GetTypeName(type); | |
string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName); | |
return SelectSingleNode(selectExpression); | |
} | |
private static string GetTypeName(Type type) | |
{ | |
string name = type.FullName; | |
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 genericTypeName = genericType.FullName; | |
// Trim the generic parameter counts from the name | |
genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); | |
string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray(); | |
name = String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", genericTypeName, String.Join(",", argumentTypeNames)); | |
} | |
if (type.IsNested) | |
{ | |
// Changing the nested type name from OuterType+InnerType to OuterType.InnerType to match the XML documentation syntax. | |
name = name.Replace("+", "."); | |
} | |
return name; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment