The documentation for enabling XmlDoc integration into your Web Api projects appears to only handle situations where all of your API types are part of your WebApi project. In particular, it discusses how to reroute the XML documentation to App_Data/XmlDocument.xml
and uncommenting a line in your config that will consume that file. This implicitly only allows for one project's documentation file.
However, in my setup I have my request and response types defined in a common "Models" project. This means that if I have an endpoint defined such as:
[Route("auth/openid/login")] public async Task<AuthenticationResponse> Login(OpenIdLoginRequest request) { ... }
Where OpenIdLoginRequest
is defined in a separate C# project like so:
public class OpenIdLoginRequest { /// <summary> /// Represents the OpenId provider that authenticated the user. (i.e. Facebook, Google, etc.) /// </summary> [Required] public string Provider { get; set; } ... }
Despite the XML doccomments, the properties of the request
parameter contain no documentation when you view the endpoint-specific help page (i.e. http://localhost/Help/Api/POST-auth-openid-login
).
How can I make it so that types in subprojects with XML documentation are surfaced in the Web API XML documentation?
Now enable XML documentation. In Solution Explorer, right-click the project and select Properties. Select the Build page. Under Output, check XML documentation file.
The C# compiler produces an XML file that contains structured data representing the comments and the API signatures. Other tools can process that XML output to create human-readable documentation in the form of web pages or PDF files, for example.
There is no built-in way to achieve this. However, it requires only a few steps:
Enable XML documentation for your subproject (from project properties / build) like you have for your Web API project. Except this time, route it directly to XmlDocument.xml
so that it gets generated in your project's root folder.
Modify your Web API project's postbuild event to copy this XML file into your App_Data
folder:
copy "$(SolutionDir)SubProject\XmlDocument.xml" "$(ProjectDir)\App_Data\Subproject.xml"
Where Subproject.xml
should be renamed to whatever your project's name is plus .xml
.
Next open Areas\HelpPage\App_Start\HelpPageConfig
and locate the following line:
config.SetDocumentationProvider(new XmlDocumentationProvider( HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
This is the line you initially uncommented in order to enable XML help documentation in the first place. Replace that line with:
config.SetDocumentationProvider(new XmlDocumentationProvider( HttpContext.Current.Server.MapPath("~/App_Data")));
This step ensures that XmlDocumentationProvider
is passed the directory that contains your XML files, rather than the specific XML file for your project.
Finally, modify Areas\HelpPage\XmlDocumentationProvider
in the following ways:
a. Replace the _documentNavigator
field with:
private List<XPathNavigator> _documentNavigators = new List<XPathNavigator>();
b. Replace the constructor with:
public XmlDocumentationProvider(string appDataPath) { if (appDataPath == null) { throw new ArgumentNullException("appDataPath"); } var files = new[] { "XmlDocument.xml", "Subproject.xml" }; foreach (var file in files) { XPathDocument xpath = new XPathDocument(Path.Combine(appDataPath, file)); _documentNavigators.Add(xpath.CreateNavigator()); } }
c. Add the following method below the constructor:
private XPathNavigator SelectSingleNode(string selectExpression) { foreach (var navigator in _documentNavigators) { var propertyNode = navigator.SelectSingleNode(selectExpression); if (propertyNode != null) return propertyNode; } return null; }
d. And last, fix all compiler errors (there should be three) resulting in references to _documentNavigator.SelectSingleNode
and remove the _documentNavigator.
portion so that it now calls the new SelectSingleNode
method we defined above.
This Last step is what modifies the document provider to support looking within multiple XML documents for the help text rather than just the primary project's.
Now when you examine your Help documentation, it will include XML documentation from types in your related project.
I ran into this too, but I didn't want to edit or duplicate any of the generated code to avoid problems later.
Building on the other answers, here's a self-contained documentation provider for multiple XML sources. Just drop this into your project:
/// <summary>A custom <see cref="IDocumentationProvider"/> that reads the API documentation from a collection of XML documentation files.</summary> public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider { /********* ** Properties *********/ /// <summary>The internal documentation providers for specific files.</summary> private readonly XmlDocumentationProvider[] Providers; /********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="paths">The physical paths to the XML documents.</param> public MultiXmlDocumentationProvider(params string[] paths) { this.Providers = paths.Select(p => new XmlDocumentationProvider(p)).ToArray(); } /// <summary>Gets the documentation for a subject.</summary> /// <param name="subject">The subject to document.</param> public string GetDocumentation(MemberInfo subject) { return this.GetFirstMatch(p => p.GetDocumentation(subject)); } /// <summary>Gets the documentation for a subject.</summary> /// <param name="subject">The subject to document.</param> public string GetDocumentation(Type subject) { return this.GetFirstMatch(p => p.GetDocumentation(subject)); } /// <summary>Gets the documentation for a subject.</summary> /// <param name="subject">The subject to document.</param> public string GetDocumentation(HttpControllerDescriptor subject) { return this.GetFirstMatch(p => p.GetDocumentation(subject)); } /// <summary>Gets the documentation for a subject.</summary> /// <param name="subject">The subject to document.</param> public string GetDocumentation(HttpActionDescriptor subject) { return this.GetFirstMatch(p => p.GetDocumentation(subject)); } /// <summary>Gets the documentation for a subject.</summary> /// <param name="subject">The subject to document.</param> public string GetDocumentation(HttpParameterDescriptor subject) { return this.GetFirstMatch(p => p.GetDocumentation(subject)); } /// <summary>Gets the documentation for a subject.</summary> /// <param name="subject">The subject to document.</param> public string GetResponseDocumentation(HttpActionDescriptor subject) { return this.GetFirstMatch(p => p.GetResponseDocumentation(subject)); } /********* ** Private methods *********/ /// <summary>Get the first valid result from the collection of XML documentation providers.</summary> /// <param name="expr">The method to invoke.</param> private string GetFirstMatch(Func<XmlDocumentationProvider, string> expr) { return this.Providers .Select(expr) .FirstOrDefault(p => !String.IsNullOrWhiteSpace(p)); } }
...and enable it in your HelpPageConfig
with the paths to the XML documents you want:
config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Api.xml"), HttpContext.Current.Server.MapPath("~/App_Data/Api.Models.xml")));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With