StackOverflow Adventures: Add custom info to Web API help page

Akos Nagy
May 1, 2017

I like stackoverflow. It provides me a way to keep in touch with the community, meet other schools of thought and test my knowledge on occasion. I like the interesting problems and questions.

Take this one for example: http://stackoverflow.com/questions/42691154/provide-documentation-for-asp-net-web-api-method-with-a-dynamic-query-string

The question goes like this: I'm using Help Pages for ASP.NET Web API to create documentation for our web api. Everything is working fine using the XML documentation comments. However, for one method I can't figure out how to supply documentation for a dynamic query string. The method uses the GetQueryNameValuePairs() of the request to select the key-value pairs of the query string to a model.

(You can check out my answer at the link).

Let's take a step back and look at the problem from a more general point of view: how can the Web API help page be extended?

Simple: add your own code :) If you check out a Web API project, under Areas you'll find Help, which contains the actual source code for the help page generation. So if you want to change what information is displayed or how, you can just modify the code in this folder.

Displaying dynamic query parameters

So if we go back to the original question, I suggested the questioner create an attribute to describe the possible query parameters:

public class DynamicUriParameterAttribute : Attribute
{
  public string Name { get; set; }
  public Type Type { get; set; }
  public string Description { get; set; }
}

Then decorate the action method with this attribute for each possible query parameter:

[DynamicUriParameter(Description = "Some description", Name ="Some name", Type =typeof(string))]
public IEnumerable<string> Get()
{
  return new string[] { "value1", "value2" };
}

Finally, modify the help page generation. Find the HelpPageConfigurationExtensions.GenerateApiModel() method: this is responsible for building the help page model:

private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
{
   HelpPageApiModel apiModel = new HelpPageApiModel()
   {
      ApiDescription = apiDescription,
   };

   ModelDescriptionGenerator modelGenerator =
                      config.GetModelDescriptionGenerator();
   HelpPageSampleGenerator sampleGenerator =
                      config.GetHelpPageSampleGenerator();            
   GenerateUriParameters(apiModel, modelGenerator);
   GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
  GenerateResourceDescription(apiModel, modelGenerator);
  GenerateSamples(apiModel, sampleGenerator);

  return apiModel;
}

You can see that the parameters are generated here. So what you have to do is add your own custom code to extend the list of parameters with the ones that are described by the attribute:

private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
{
   HelpPageApiModel apiModel = new HelpPageApiModel()
   {
      ApiDescription = apiDescription,
   };


  ModelDescriptionGenerator modelGenerator =
                       config.GetModelDescriptionGenerator();
  HelpPageSampleGenerator sampleGenerator =
                       config.GetHelpPageSampleGenerator();            
  GenerateUriParameters(apiModel, modelGenerator);

  // add this part
  var attrs = apiDescription.ActionDescriptor
           .GetCustomAttributes<DynamicUriParameterAttribute>();

  foreach (var attr in attrs)
  {
    apiModel.UriParameters.Add(
       new ParameterDescription
       {
           Name = attr.Name,
           Documentation = attr.Description,
           TypeDescription = modelGenerator.GetOrCreateModelDescription(attr.Type)
       }
     );
  }
  // until here
  GenerateRequestModelDescription(apiModel, modelGenerator, sampleGenerator);
  GenerateResourceDescription(apiModel, modelGenerator);
  GenerateSamples(apiModel, sampleGenerator);

  return apiModel;
}

Just query the attributes on the ActionDescriptor, and if you find some, add a UriParameter manually to the list. Use the modelGenerator to generate the documentation for the parameter type (which, of course, comes from attribute).

Displaying authorization info

Another helpful thing that you can do is display authorization info.
Now you know how to query for extra attributes on action methods. How can you add extra information to the help page?

The first step is to extend the model. Find the HelpPageApiModel class and add your custom properties. Whatever you want.

/// <summary>
/// The model that represents an API displayed on the help page.
/// </summary>
public class HelpPageApiModel
{

  /// <summary>
  /// Initializes a new instance of the <see cref="HelpPageApiModel"/> class.
  /// </summary>
  public HelpPageApiModel()
  {
     UriParameters = new Collection<ParameterDescription>();
     SampleRequests = new Dictionary<MediaTypeHeaderValue, object>();
     SampleResponses = new Dictionary<MediaTypeHeaderValue, object>();
     ErrorMessages = new Collection<string>();
   }

   // This is my property to describe whether authorization is required
   public bool AuthRequired { get; set; }

Once you have a place to put the extra info, you can again go ahead and modify the HelpPageConfigurationExtensions class. Add a new method:

private static bool CheckForAuth(ApiDescription apiDescription)
        {
            return apiDescription.ActionDescriptor.GetCustomAttributes<AuthorizeAttribute>().Count != 0 ||
                   apiDescription.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeAttribute>().Count != 0;
        }

Given an ApiDescription, this method checks whether the action method represented by the API or the controller that the action method belongs to have an AuthorizeAttribute.
Now you can use this method to initialize your new property when creating the api model:

private static HelpPageApiModel GenerateApiModel(ApiDescription apiDescription, HttpConfiguration config)
{
  HelpPageApiModel apiModel = new HelpPageApiModel()
  {
    ApiDescription = apiDescription,
    // initialize using your new method
    AuthRequired = CheckForAuth(apiDescription)
  };
  // other code still here
           
  return apiModel;
}

Finally, you can go ahead and modify the view for this model. Finding the right view can be tricky. For this example the best place would be the HelpPageApiModel.cshtml in Views/Help/DisplayTemplates (this is probably the best for most action level information). Once you found it, add your custom info to wherever you'd like.

@*This tag was already here*@
<h2>Request Information</h2>

@*This is our new piece of information*@
@if (Model.AuthRequired)
{
  <h3>Authorization required.</h3>
}

And there you have it:

So you can go ahead and include all kinds of custom information to your help pages. The idea is always the same :)

Akos Nagy