Opt in and opt out from ASP.NET Web API Help Page

Β· 741 words Β· 4 minutes to read

The autogenerated ASP.NET Web API help page is an extremely useful tool for documenting your Web API. It can not only present information about the routes, but also show sample requests and responses in all of supported media type formats, and even display information for DataAnnotations.

However, more often than not, you don’t want all endpoints to be visible in the help page. Let’s have a look at how you can opt in and opt out from the ASP.NET Web API Help Page with your resources.

Opt out πŸ”—

This is actually the default approach and that’s supported out of the box with Web API. As soon as you generate a new help page, everything is included in it - every single possible route, that is.

You can opt out with a specific controller or action by using the built-in ApiExplorerSettingsAttribute and setting IgnoreApi property to true. In the following example - the first controller is entirely excluded from the Help Page, and the second controller only has the Post method excluded.

[ApiExplorerSettings(IgnoreApi = true)]  
public class ValuesController : ApiController  
{  
public string Get(int id)  
{  
return "value";  
}

public void Post([FromBody]string value)  
{  
}  
}

public class ValuesController : ApiController  
{  
public string Get(int id)  
{  
return "value";  
}

[ApiExplorerSettings(IgnoreApi = true)]  
public void Post([FromBody]string value)  
{  
}  
}  

Opt in πŸ”—

Now, a more interesting case is this: what if you want to exclude everything, and only opt in with some specific cases? This is a question asked recently by my friend Andrew, and he proposed introducing a reverse attribute - IncludeInApiExplorerAttribute (note: ideally we’d just extend ApiExplorerSettingsAttribute but it’s, surprise, surprise - sealed).

The attribute doesn’t need to have any behavior - it can be just a marker:

public class IncludeInApiExplorerAttribute : Attribute { }  

We can now proceed to implementing a custom ApiExplorer - which is a Web API service responsible for walking through your controllers and actions and gathering information about them for the help page. We will override the ShouldExploreAction and introduce the following logic:

    • if action is decorated with IncludeInApiExplorerAttribute and not decorated with NonActionAttribute, it should be included in the Help Page
    • alternatively, if action does not have IncludeInApiExplorerAttribute and is not decorated with NonActionAttribute, we will go up one level and check the controller if it has an IncludeInApiExplorerAttribute - if that’s the case then it means all actions should be included

The code is shown below. Note that the default ApiExplorer uses a Regex utility function to match route constraints. Since that method is private and we can use it in our subclassed OptInApiExplorer, I copied it over.

public class OptInApiExplorer : ApiExplorer  
{  
public OptInApiExplorer(HttpConfiguration configuration)  
: base(configuration)  
{

}

public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)  
{  
var includeAttribute = actionDescriptor.GetCustomAttributes<IncludeInApiExplorerAttribute>().FirstOrDefault();  
var nonAction = actionDescriptor.GetCustomAttributes<NonActionAttribute>().FirstOrDefault();

if (includeAttribute == null && nonAction == null)  
{  
var includeAttributeOnController = actionDescriptor.ControllerDescriptor.GetCustomAttributes<IncludeInApiExplorerAttribute>().FirstOrDefault();  
return includeAttributeOnController != null && MatchRegexConstraint(route, "action", actionVariableValue);  
}

return includeAttribute != null && nonAction == null && MatchRegexConstraint(route, "action", actionVariableValue);  
}

private static bool MatchRegexConstraint(IHttpRoute route, string parameterName, string parameterValue)  
{  
IDictionary<string, object> constraints = route.Constraints;  
if (constraints != null)  
{  
object constraint;  
if (constraints.TryGetValue(parameterName, out constraint))  
{  
string constraintsRule = constraint as string;  
if (constraintsRule != null)  
{  
string constraintsRegEx = "^(" + constraintsRule + ")$";  
return parameterValue != null && Regex.IsMatch(parameterValue, constraintsRegEx, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);  
}  
}  
}

return true;  
}  
}  

And that’s it. You should now register the new API explorer against your HttpConfiguration:

httpConfiguration.Services.Replace(typeof(IApiExplorer), new OptInApiExplorer(config));  

You can start marking your controllers and actions with the new reverse attribute. By default, everything is excluded now. If you have a look at the following example, the first controller is now entirely included in the Help Page, while the second controller only has the Get action show up there.

[IncludeInApiExplorer]  
public class ValuesController : ApiController  
{  
public string Get(int id)  
{  
return "value";  
}

public void Post([FromBody]string value)  
{  
}  
}

public class ValuesController : ApiController  
{  
[IncludeInApiExplorer]  
public string Get(int id)  
{  
return "value";  
}

public void Post([FromBody]string value)  
{  
}  
}  

Reversing the API explorer look up process can be sometimes beneficial (i.e. the majority of your endpoints should not be publicized) - IMHO there should be a switch on the default API explorer that allows you to configure it be opt-out or opt-in. Maybe this will be one day brough to the Web API core, but until then - you can use this workaround.

About


Hi! I'm Filip W., a cloud architect from ZΓΌrich πŸ‡¨πŸ‡­. I like Toronto Maple Leafs πŸ‡¨πŸ‡¦, Rancid and quantum computing. Oh, and I love the Lowlands 🏴󠁧󠁒󠁳󠁣󠁴󠁿.

You can find me on Github and on Mastodon.

My Introduction to Quantum Computing with Q# and QDK book
Microsoft MVP