162 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			162 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C#
		
	
	
	
|  | 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; | ||
|  | using WebAPI.Areas.HelpPage.ModelDescriptions; | ||
|  | 
 | ||
|  | namespace WebAPI.Areas.HelpPage | ||
|  | { | ||
|  |     /// <summary> | ||
|  |     /// A custom <see cref="IDocumentationProvider"/> that reads the API documentation from an XML documentation file. | ||
|  |     /// </summary> | ||
|  |     public class XmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider | ||
|  |     { | ||
|  |         private XPathNavigator _documentNavigator; | ||
|  |         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="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 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 = _documentNavigator.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 _documentNavigator.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 _documentNavigator.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; | ||
|  |         } | ||
|  |     } | ||
|  | } |