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:

The binaries can be downloaded at:

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)
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }

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


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


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


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


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.


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




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

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: }
   7: function PostUsers(user) {
   8:     return $.ajax({
   9:         url: "/api/Users",
  10:         type: "POST",
  11:         contentType: "application/json",
  12:         data: JSON.stringify(user)});
  13: }
  15: function GetValues() {
  16:     return $.ajax({
  17:         url: "/api/Values",
  18:         type: "GET"});
  19: }
  21: function GetValues1(id) {
  22:     return $.ajax({
  23:         url: "/api/Values/" + id,
  24:         type: "GET"});
  25: }
  27: function PostValues(value) {
  28:     return $.ajax({
  29:         url: "/api/Values",
  30:         type: "POST",
  31:         contentType: "application/json",
  32:         data: JSON.stringify(value)});
  33: }
  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: }
  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.





Comments (39)

  1. Martin J. says:

    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 ;)).

  2. Abhijeet P says:

    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.…/generating-version-specific-help-documentation-pages-for-asp-net-web-api-applica

    Thanks very much again.

  3. Steven Suing says:

    Just a thanks for putting this together. Having separate docs and service is important for us.

  4. Wei Q says:

    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?

  5. Wei Q says:

    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!

  6. Yao - MSFT says:

    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.

  7. Yao - MSFT says:

    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.

  8. KP says:

    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?

  9. Yao - MSFT says:

    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.

  10. I get an error generating the JS file(s)

    I have downloaded/extracted the dll file (

    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.

  11. Yao - MSFT says:

    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.

  12. Great.  Unblocking the dll worked!  Thanks Yao.

  13. RobertR says:

    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?

  14. Yao - MSFT says:

    Hi RobertR,

    Where are you getting the WebHost package that is >=5.0? From the nightly build (…/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.

  15. Peter says:

    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?

  16. Yao - MSFT says:

    Hi Peter,

    You can get the source code and edit the (similar to Index.cshtml). Simply replace the following line:

    ILookup<string, ApiDescription> apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor.ControllerName);

    With something like:

    ILookup<string, ApiDescription> apiGroups = Model.OrderBy(d => d.ActionDescriptor.ControllerDescriptor.ControllerName).ToLookup(api => api.ActionDescriptor.ControllerDescriptor.ControllerName);

    Hope this helps,


  17. 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

  18. gcapnias says:

    Yao, would it be possible to post on the source code of WebApiJsClientGenerator.dll as part of the solution you have uploaded in

    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…

  19. Yao - MSFT says:

    Hi George,

    Sure. I'll try update the sample to include the WebApiJsClientGenerator this weekend.

  20. Eckard says:

    Is it possible to use areas with web api and have the help page documentation working ?

  21. Bob Armour says:


    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…


    …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.


  22. Maya says:

    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:


    -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?

  23. Pyledriver says:

    I too get the same message as Maya in the previous post

    "Error: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."

    The exception is thrown from the following line in the HttpConfigurationImporter class

    "Action<HttpConfiguration> registerConfig = Delegate.CreateDelegate(typeof(Action<HttpConfiguration>), registerConfigMethod) as Action<HttpConfiguration>;"

    I have updated my project to use Web API 2.0 – Is your library compatible with Web API 2.0?



  24. Richard Smith says:

    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.

  25. Zafar Ansari says:

    Could you please send me the updated Help page generator which works with latest version of web api my email address is   thanks

  26. George Chung says:

    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.Lazy`1.CreateValue()

      at System.Lazy`1.LazyInitValue()

      at System.Web.Http.Description.ApiExplorer.get_ApiDescriptions()

      at WebApiHelpPageGenerator.Program.Main(String[] args)

  27. Ram Naresh Talluri says:

    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.

  28. Proress says:

    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

  29. David Brown says:

    Hi Yao,

    excellent work!  Have the Issue Maya/Richard el al are having above with the error message 'Cannor bind to Target…'  I'm using Web API 2.0.  

    Richard, thanks for the heads up regarding your work around.  On;ly questions is that the last 'add config..EnsureInitialized()' step, there is no 'EnsureInitialized' method on config.  

    Any heads up/help appreciated in advance

    Thanks much


  30. JyotiG says:

    I was getting the same error as Richard Smith. I was able to follow his instructions including the call to config.EnsureInitialized. It builds fine but I get the following error in the call to config.Services.GetApiExplorer().ApiDescriptions

    Could not load file or assembly 'System.Web.Http, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified.

    Anyone seen this and knows how to fix it?

  31. vanderke says:

    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=, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">




    That seemed to fix the issue for me. I hope this helps out others who may have this problem.

  32. Raymundo says:

    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."

  33. Adrian says:


    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?

  34. Rast says:

    @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)

  35. Subodh says:

    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.

  36. Lalosoft says:

    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:…/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

  37. iCediCe says:

    Thank you Lalosoft, that was very useful. I stole your solution and modified to my needs.

  38. NanditaR says:

    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.

  39. NanditR says:

    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?