简体   繁体   中英

How do I get ASP.NET Web API working with versioning and the Help Page extension?

I've implemented a versioning framework into my WebAPI application, and would very much like to get it working with the new Help Page extension from Microsoft.

Microsoft.AspNet.WebApi.HelpPage

SDammann.WebApi.Versioning

Quite simply, I don't know how to get them both working together. I have 2 projects:

  • AdventureWorks.Api (The main host/root application)
  • AdventureWorks.Api.v1 (A class library containing the first version of the API)

The versioning works as expected.

I've tried installing the HelpPage package on the root application, and when I browse to the help page, it appears none of the controllers are being found. Internally I believe it uses:

Configuration.Services.GetApiExplorer().ApiDescriptions

This returns no results, so I get an error.

Can anyone assist me in getting both of these packages working together?

Edit: In the beginning, I wasn't sure this was a routing problem, but recent comments seem to suggest otherwise. Here is my RouteConfig.cs

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapHttpRoute(
            name: "SldExportAliasApi",
            routeTemplate: "api/v{version}/sld-export/{id}",
            defaults: new { id = RouteParameter.Optional, controller = "Export" }
        );

        routes.MapHttpRoute(
            name: "LinkRoute",
            routeTemplate: "api/v{version}/link/{system}/{deployment}/{view}",
            defaults: new { controller = "Link" }
        );

        routes.MapHttpRoute(
             name: "DefaultSubParameterApi",
             routeTemplate: "api/v{version}/{controller}/{id}/{param}",
             defaults: new { id = RouteParameter.Optional, param = RouteParameter.Optional }
        );

        routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/v{version}/{controller}/{action}/{id}",
            defaults: new { action = "Index", id = RouteParameter.Optional }
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

You need to get a documentation XML file from your project AdventureWorks.Api.v1 project and place it in the bin folder of the AdventureWorks.Api project:

Then add these lines to your Application_Start method:

// enable API versioning
        GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new RouteVersionControllerSelector(GlobalConfiguration.Configuration));
        GlobalConfiguration.Configuration.Services.Replace(typeof(IApiExplorer), new VersionedApiExplorer(GlobalConfiguration.Configuration));
        GlobalConfiguration.Configuration.Services.Replace(typeof(IDocumentationProvider),
                                new XmlCommentDocumentationProvider(System.IO.Path.GetDirectoryName(
                                    System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) +
                                                                    "\\Adventure.Api.v1.XML"));

Then you can view your API with the documentation.

Sometimes the version number does not get to be picked up correctly, and replaced by ???

To fix this add:

if (api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace != null)
    {
        var versionName = api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace.Replace(".Controllers", "").Split('.').Last();
        api.RelativePath = api.RelativePath.Replace("v???", versionName);
    }

to your ApiGroup.cshtml exactly at this place:

@foreach (var api in Model)
{
    if (api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace != null)
    {
        var versionName = api.ActionDescriptor.ControllerDescriptor.ControllerType.Namespace.Replace(".Controllers", "").Split('.').Last();
        api.RelativePath = api.RelativePath.Replace("v???", versionName);
    }
    <tr>
        <td class="api-name"><a href="@Url.Action("Api", "Help", new { apiId = api.GetFriendlyId() })">@api.HttpMethod.Method @api.RelativePath</a></td>
        <td class="api-documentation">
        @if (api.Documentation != null)
        {
            <p>@api.Documentation</p>
        }
        else
        {
            <p>No documentation available.</p>
        }
        </td>
    </tr>
}

This should do the trick!

I couldn't figure out how to comment on a post :( I think this should probably be a comment under the marked answer for this question but SDamman is updated and all I needed to do was this

// enable API versioning                   
GlobalConfiguration.Configuration.Services.Replace(typeof(System.Web.Http.Dispatcher.IHttpControllerSelector), 
     new SDammann.WebApi.Versioning.RouteVersionedControllerSelector(GlobalConfiguration.Configuration));

GlobalConfiguration.Configuration.Services.Replace(typeof(IApiExplorer), new SDammann.WebApi.Versioning.VersionedApiExplorer(GlobalConfiguration.Configuration));

There is a type called VersionedApiExplorer and it works great. Hope this helps the solution is much easier now.

EDIT: I realized after trying to get help working again myself that my answer wasn't obvious at all.

The ONLY thing you need to do to get help pages working is replace the global configs IApiExplorer, that's it. Just do it right after you change the handler per sdammans instructions.

I agree with @mortware, the default routing for web api would mean your url should look something like "site/api/controllerName/" if you're using default Get() / Post() methods. If you're using specificly named methods then the route looks something like "site/api/controllerName/methodName".

I've also run into difficulty with the parameter names. Eg, if in your route specified in /App_Start/WebApiConfig.cs you have;

// Controller with ID
// To handle routes like `/api/VTRouting/1`
config.Routes.MapHttpRoute(
    name: "ControllerAndId",
    routeTemplate: "api/{controller}/{id}",
    defaults: null,
    constraints: new { id = @"^\d+$" } // Only integers 
);

// Controllers with Actions
// To handle routes like `/api/VTRouting/route`
config.Routes.MapHttpRoute(
    name: "ControllerAndAction",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: null,
    constraints: new { id = @"^\d+$" } // Only integers 
);

then the method parameter for your http verb must have a parameter called "id", eg;

// url: site/api/controller/<int>
public HttpResponseMessage Get(int id) { return null; /*dummy*/ }

// url: site/api/controller/<int>
public HttpResponseMessage Post(int id) { return null; /*dummy*/ }

// url: site/api/controller/SomeAction/<int>
public HttpResponseMessage SomeAction(int id) { return null; /*dummy*/ }

If you have something like;

public HttpResponseMessage Get(int myID) { return null; /*dummy*/ }

It will not work as the "myID" parameter doesn't match that {id} specified in the route. As @OakNinja pointed out, we'll need your routing in the WebApiConfig.cs to help you pinpoint the exact cause

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM