ASP.NET Web Api Routing Customization

2019-04-04 14:12发布

问题:

I have WebApi controllers that end with the "Api" suffix in their names (For ex: StudentsApiController, InstructorsApiController). I do this to easily differentiate my MVC controllers from WebApi controllers. I want my WebApi routes to look similar to

http://localhost:50009/api/students/5 and not http://localhost:50009/api/studentsapi/5.

Currently to achieve this, I am setting up routes like

routes.MapHttpRoute(
name: "GetStudents",
routeTemplate: "api/students/{id}",
defaults: new { controller = "StudentsApi", id = RouteParameter.Optional });

routes.MapHttpRoute(
name: "GetInstructors",
routeTemplate: "api/instructors/{id}",
defaults: new { controller = "InstructorsApi", id = RouteParameter.Optional });

This is turning out to be very cumbersome as I have to add a route for each method in my controllers. I am hoping there should be an easy way to setup route templates that automatically adds the "api" suffix the controller name while processing routes.

回答1:

I think the extensibility point you're looking for is the controller selector. You can create a class that derives from DefaultHttpControllerSelector and overrides the GetControllerName to strip out the "api" part. You can then register this controller selector on your service's configuration Services.



回答2:

Following @Youssef Moussaoui's direction I ended up writing the following code that solved the problem.

public class ApiControllerSelector : DefaultHttpControllerSelector
{
    public ApiControllerSelector(HttpConfiguration configuration)
        : base(configuration)
    {
    }

    public override string GetControllerName(HttpRequestMessage request)
    {
        if (request == null)
            throw new ArgumentNullException("request");

        IHttpRouteData routeData = request.GetRouteData();

        if (routeData == null)
            return null;

        // Look up controller in route data
        object controllerName;
        routeData.Values.TryGetValue("controller", out controllerName);

        if (controllerName != null)
            controllerName += "api";

        return (string)controllerName;
    }
}

And register it in Global.asax as

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
            new ApiControllerSelector(GlobalConfiguration.Configuration));


回答3:

Now that ASP.NET Web API 2 is out, there is a much less cumbersome way to do more complex routing like that you suggested, by using attribute routing.

At the top of your controller just add the following attribute:

[RoutePrefix("api/students")]
public class StudentsApiController : ApiController
{
    ...
}

And then before each API method:

[Route("{id}"]
public HttpResponseMessage Get(int id)
{
    ...
}

There is a bit of setup required, but the positives of doing routing this way are many. For one, you can put the routing with the controllers and methods that do the actual work, so you're never searching around wondering if you have the right route. Secondly and more importantly, it's much easier to do more complex routing, like having the controller name different from the route name (like you want) or having very complex patterns to match against.



回答4:

Following Youssef's comment on muruug's answer would look something like this

public class ApiControllerSelector : DefaultHttpControllerSelector
{
    public ApiControllerSelector (HttpConfiguration configuration) : base(configuration) { }

    public override string GetControllerName(HttpRequestMessage request)
    {
        return base.GetControllerName(request) + "api";
    }
}