Design-time generation of help page (or client) for ASP.NET Web API

All the help page samples I’ve shown you so far are pretty much generated at runtime, meaning the help page is generated after you start the application. Today, I’m going to show you that it doesn’t always need to be generated at runtime. In fact, it can be generated at design-time. Yes, before you host and start the application!

This is especially useful when you want to provide any kind of tooling for Web API. E.g. generating proxies for your Web APIs as part of the build process. Another scenario that can benefit from this is when you want to host the help page separately from your application – you can simply generate the help page as HTML files and then host the generated files anywhere that supports HTML.

To show you how this can be done, I’ve created a simple command-line utility (WebApiHelpPageGenerator.exe) that will statically generate the help page as HTML files. All you need to provide is the path to you application assembly.

The source code can be downloaded at: https://code.msdn.microsoft.com/Design-time-help-page-3048fb43

The binaries can be downloaded at: WebApiHelpPageGenerator.zip

Using the WebApiHelpPageGenerator.exe

The WebApiHelpPageGenerator.exe takes one required parameter (-p) which is the path to your application assembly.

WebApiHelpPageGenerator.exe -p MvcApplication1.dll

You do need to follow a convention in order to opt-in: Having a public static “WebApiConfig” class with a static “Register” method where the API routes are specified. This is the pattern that we have in the default Web API templates.

 public static class WebApiConfig
 {
     public static void Register(HttpConfiguration config)
     {
         config.Routes.MapHttpRoute(
             name: "DefaultApi",
             routeTemplate: "api/{controller}/{id}",
             defaults: new { id = RouteParameter.Optional }
         );
     }
 }

If everything goes well, you will see the following HTML files generated.

image

You can open the HTML files and even browse through them locally.

image

Clicking on “GET api/Values” will take you to the “GET-api-values.html”.

image

Including Controllers from different assembly

Very often you can have the controllers defined on different assemblies. In order to get all of them, simply use the -r flag.

WebApiHelpPageGenerator.exe -p MvcApplication1.dll –r ControllerLibrary1.dll ControllerLibrary2.dll

image

Generating proxy or other types of documentation

You can extend the WebApiHelpPageGenerator.exe to generate proxy/client or other types of documentation.

To do this, first you need to add the reference to WebApiHelpPageGenerator.exe.

image

Then, implement the IOutputGenerator. For illustration, I simply printed things out to the console but you can use any templating engine to generate the documentation and save it to files.

 using System;
 using WebApiHelpPageGenerator;
  
 namespace MyExtension
 {
     public class MyOutputGenerator : IOutputGenerator
     {
         public void GenerateIndex(System.Collections.ObjectModel.Collection<System.Web.Http.Description.ApiDescription> apis)
         {
             Console.WriteLine("Generating index.");
         }
  
         public void GenerateApiDetails(WebApiHelpPage.Models.HelpPageApiModel apiModel)
         {
             Console.WriteLine("Generating something for {0}", apiModel.ApiDescription.ID);
         }
     }
 }

Next, just use the -e option to load the assembly where the custom IOutputGenerator is defined.

WebApiHelpPageGenerator.exe -p MvcApplication1.dll -e MyExtension.dll

image

 

Update

I’ve implemented a custom IOutputGenerator that will generate a simple JavaScript client. You can download the binary here: WebApiJsClientGenerator.zip

To try it, simply use the -e option.

WebApiHelpPageGenerator.exe -p MvcApplication1.dll -e WebApiJsClientGenerator.dll

It will spit out a file that look like this:

    1: function GetUsers(id) {
    2:     return $.ajax({
    3:         url: "/api/Users/" + id,
    4:         type: "GET"});
    5: }
    6:  
    7: function PostUsers(user) {
    8:     return $.ajax({
    9:         url: "/api/Users",
   10:         type: "POST",
   11:         contentType: "application/json",
   12:         data: JSON.stringify(user)});
   13: }
   14:  
   15: function GetValues() {
   16:     return $.ajax({
   17:         url: "/api/Values",
   18:         type: "GET"});
   19: }
   20:  
   21: function GetValues1(id) {
   22:     return $.ajax({
   23:         url: "/api/Values/" + id,
   24:         type: "GET"});
   25: }
   26:  
   27: function PostValues(value) {
   28:     return $.ajax({
   29:         url: "/api/Values",
   30:         type: "POST",
   31:         contentType: "application/json",
   32:         data: JSON.stringify(value)});
   33: }
   34:  
   35: function PutValues(id, value) {
   36:     return $.ajax({
   37:         url: "/api/Values/" + id,
   38:         type: "PUT",
   39:         contentType: "application/json",
   40:         data: JSON.stringify(value)});
   41: }
   42:  
   43: function DeleteValues(id) {
   44:     return $.ajax({
   45:         url: "/api/Values/" + id,
   46:         type: "DELETE"});
   47: }

* If you’re getting error like “Could not load file or assembly…”, it means that the WebApiJsClientGenerator.dll file is blocked. To unblock it, go to file Properties and click Unblock. After that, the error should go away.

image

 

Enjoy!

Yao

Comments

  • Anonymous
    January 24, 2013
    It is great reading that something like this exists. :) I used a very similar approach already for quite some time. I have created a build task that takes a path to an Assembly containing a Web API. It'll also call the static register method, generate an IApiExplorer out of that and then generate and output a proxy client class. After some revisions it works quite good and is very helpful, whenever the service assembly is compiled the clients are regenerated (but only when the service changed ;)).

  • Anonymous
    February 03, 2013
    Thanks Yao. The autogen of the docs is simply great! Is there a way to have the Web API help page package work properly with web api versioning package? I have posted a question for this on StackOverflow to illustrate the issue I am facing. I would appreciate your thoughts and insights on this. stackoverflow.com/.../generating-version-specific-help-documentation-pages-for-asp-net-web-api-applica Thanks very much again.

  • Anonymous
    February 05, 2013
    Just a thanks for putting this together. Having separate docs and service is important for us.

  • Anonymous
    February 12, 2013
    Does the helppage work with OData on top of Web API? I have a simple Odata service with the OData nightly built. The service API help page is not displaying and there isn't any error message. What I need to do to get it to work with OData?

  • Anonymous
    February 15, 2013
    Yao, the helppage is working for my Odata service now after I uncommented the default MapHttpRoutes that came with the project template. I didn't need it for my Odata service. It seems teh helppage requires it to be here. However I noticed the URL to the help page doesn't really change if I change this routeTemplate from "api/{Controller}/...." to "whateveroremptyhere/{Controller}/....". Does this routeTemplate affect the URL to the helppage? Thanks!

  • Anonymous
    February 17, 2013
    Hi Abhijeet P, Implementing a custom ApiExplorer would be the way to go. That said, you might not need to implement it from scratch, you can just compose the default ApiExplorer implementation and add some post-processing logic to group the APIs by version number and remove the version number from the RelativePath.

  • Anonymous
    February 17, 2013
    Hi Wei Q, The routeTemplate you pointed out doesn't affect the help page URL (note that the default URL is /help instead of /api/help). The URL to help page is actually set in HelpPageAreaRegistration.cs. I'm a little surprised that it works after adding the default Http route given that OData uses a different route. I think in some cases, the information generated might be incorrect because it's based on the Http routing instead of the OData routing. Currently the Help Page doesn't support OData routes by default. However, we'd consider providing the support in the future.

  • Anonymous
    March 07, 2013
    Yao, this is a sweet post.  thanks.  I am running into an issue, perhaps you can help out. When I generate the documentation, I get 2 listings for the same service.  So, lets say my actual service url is '/api/clients/1' which returns a details about a client, it would also generate '/api/clients/Get?id=1'.  Why is this?

  • Anonymous
    March 12, 2013
    Hi KP, Do you happen to have another route apart from the default "api/{controller}/{id}" that look like this: "api/{controller}/{action}"? That's probably causing the second listing to appear.

  • Anonymous
    March 13, 2013
    I get an error generating the JS file(s) I have downloaded/extracted the dll file (WebApiJsClientGenerator.zip) The Help files get generated OK.  Once I add the "-e" option to generate the javascript, I get an error "could not load file of assembly "WebApiJsClientGenerator.dll" or one of it's dependencies. I have the dll in the same folder as the exe file.

  • Anonymous
    March 13, 2013
    Hi SammyD, Thanks for letting me know about the problem. This is because the file is blocked by your computer. I've updated the instruction above on how to solve the issue.

  • Anonymous
    March 13, 2013
    Great.  Unblocking the dll worked!  Thanks Yao.

  • Anonymous
    March 19, 2013
    Hello Yao, right now I'd like to use help pages, but the restriction on the dependencies of WebApi.WebHost being < 4.1 is a bit of a problem since we have other NuGets in our project that need WebApi.WebHost to be >= 5.0.  I take it there's no choice but to wait for HelpPage to update to 5 at some point?

  • Anonymous
    March 21, 2013
    Hi RobertR, Where are you getting the WebHost package that is >=5.0? From the nightly build (www.myget.org/.../aspnetwebstacknightly)? In that case, you can install the help page from the nightly build as well. It should have the dependency on nightly build packages which are >=5.0.

  • Anonymous
    April 08, 2013
    Yao - When I generate the help pages, all my controllers are listed on the /doc page. However, they seem to be listed in random order.  Is there a way to sort them in alphabetical order?

  • Anonymous
    April 09, 2013
    The comment has been removed

  • Anonymous
    April 09, 2013
    Yao - I found the code you mentioned in AreasHelpPageViewsHelpIndex.cshtml. I replaced the code with your example and it worked like a charm! Thanks very much.

  • Peter
  • Anonymous
    April 21, 2013
    Yao, would it be possible to post on the source code of WebApiJsClientGenerator.dll as part of the solution you have uploaded in code.msdn.microsoft.com? I must say, I find the most interesting to create JavaScript proxy automatically! I want to try to create a automated proxy to use with knockout.js...

  • Anonymous
    April 26, 2013
    Hi George, Sure. I'll try update the sample to include the WebApiJsClientGenerator this weekend.

  • Anonymous
    July 16, 2013
    Is it possible to use areas with web api and have the help page documentation working ?

  • Anonymous
    October 09, 2013
    Yao, Thanks for this great article - the help page generation facility is really useful. One thing that I'd like to do is to ensure that API docs are not generated for controller/methods/actions for users who are not authorized to access them. i.e. If a controller or method was marked with an attribute of... [Authorize(Roles="Administrators")] ...then I'd expect that controller or method to only appear on the API help docs page if the logged in user is in the Admininstrators role. Thanks.

  • Anonymous
    October 29, 2013
    Hello Yao, It seems that the WebApiHelpPageGenerator is not working with the latest nightly builds (or maybe something with my setup? Not sure). I am running this command: C:WebApiHelpPageGenerator>WebApiHelpPageGenerator.exe -p C:PathMyDllName.dll And I get the following error: Error: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type. How can I resolve this?

  • Anonymous
    December 08, 2013
    The comment has been removed

  • Anonymous
    January 30, 2014
    If HttpConfiguration in your project doesn't reference the same version of WebApi as that used by the WebApiHelpPageGenerator then you get "Error: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type." I got it pretty much working against Web API 2 by adding the 2 new methods in XmlDocumentationProvider required by IDocumentationProvider, just getting them to return null; and adding a call at the end of HttpConfigurationImporter.ImportConfiguration to config.EnsureInitialized(). The generator doesn't work against the current version of CommandLineParser, so beware before doing a solution wide package update.

  • Anonymous
    February 07, 2014
    Could you please send me the updated Help page generator which works with latest version of web api my email address is zafar.ansari@live.com   thanks

  • Anonymous
    February 13, 2014
    When you call config.MapHttpAttributeRoutes() after calling the config.Register() method, the call to config.Services.GetApiExplorer().ApiDescriptions throws: InvalidOperationException: The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application's startup code after all other initialization code. Here's the stack. Looks we can't enumerate attribute based routes for the time being....   at System.Web.Http.Routing.RouteCollectionRoute.get_SubRoutes()   at System.Web.Http.Routing.RouteCollectionRoute.GetEnumerator()   at System.Web.Http.Description.ApiExplorer.<FlattenRoutes>d__2.MoveNext()   at System.Web.Http.Description.ApiExplorer.<FlattenRoutes>d__2.MoveNext()   at System.Web.Http.Description.ApiExplorer.InitializeApiDescriptions()   at System.Lazy1.CreateValue() &nbsp; at System.Lazy1.LazyInitValue()   at System.Web.Http.Description.ApiExplorer.get_ApiDescriptions()   at WebApiHelpPageGenerator.Program.Main(String[] args)

  • Anonymous
    August 15, 2014
    Can you guide me how to fix ? Error: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

  • Anonymous
    September 03, 2014
    Yao can you shed some light on the problem posted by Ram Naresh Talluri? Here is the failing instruction:                 Action<HttpConfiguration> registerConfig = Delegate.CreateDelegate(typeof(Action<HttpConfiguration>), registerConfigMethod) as Action<HttpConfiguration>; in the HttpConfigurationImporter.cs file

  • Anonymous
    September 17, 2014
    The comment has been removed

  • Anonymous
    November 03, 2014
    The comment has been removed

  • Anonymous
    November 24, 2014
    If you get the "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type." error, make sure that you are using the updated versions of the WebAPI Core. I simply replaced the reference the project had with this one: <Reference Include="System.Web.Http, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">  <SpecificVersion>False</SpecificVersion>  <HintPath>..packagesMicrosoft.AspNet.WebApi.Core.5.0.0libnet45System.Web.Http.dll</HintPath> </Reference> That seemed to fix the issue for me. I hope this helps out others who may have this problem.

  • Anonymous
    November 30, 2014
    vanderke, I try to add Microsoft ASP.NET Web API 5.2.2 to the project but it tells me "You are trying to install this package into a project that targets .NETFramework, Version=v4.0, but the package does not contain any assembly references or content files that are compatible with that framework."

  • Anonymous
    December 02, 2014
    Hi, I'm trying your binaries on and MVC4 application and I receive a null object when trying to get the path to the XML documentation file. It always gives me that HttpContext.Current is null. About a year ago I used the same binaries and worked just fine. Do you have any suggesions?

  • Anonymous
    December 22, 2014
    @Adrian HttpContext.Current will always be null if you are not running application under IIS. Do not use it in scenarios where you run your app without IIS (like this tool does)

  • Anonymous
    May 26, 2015
    For me document are not getting generated. While debugging source code come to know that config.Services.GetApiExplorer().ApiDescriptions always coming as 0. I modified my API project to include HelpPage nugget package as well. But still descriptors count is 0. Please advise.

  • Anonymous
    June 02, 2015
    I just asked on another thread about this very utility but it doesn't work with the latest 5.2.3 libraries. So i downloaded the code and updated it. As i didn't see a git hub for this code I created one here: github.com/.../webapi-html-help-generation and posted some updates i Made today to make the html generator work. It also has a change to the DefaultGenerator to create documentation in a sub folder called HtmlHelp

  • Anonymous
    August 17, 2015
    Thank you Lalosoft, that was very useful. I stole your solution and modified to my needs.

  • Anonymous
    January 27, 2016
    It is bery useful blog. I tried WebApiHelpPageGenerator.exe tool to generate help at design time ,  but I get following error at command prompt. Error : cacot bind to the target method because its signature or security transparancy is not compatible with that of the delegate type. Please advise.

  • Anonymous
    January 27, 2016
    hi Lalosoft,  Thank you for your solution.  I would like to use it with  Microsoft.AspNet.WebApi.HelpPage generated view pages. How did you generated .tt from .cshtml?