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.
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
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.
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 removedAnonymous
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 removedAnonymous
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 thanksAnonymous
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() at System.Lazy
1.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 fileAnonymous
September 17, 2014
The comment has been removedAnonymous
November 03, 2014
The comment has been removedAnonymous
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 HtmlHelpAnonymous
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?