MVC API Routing When Multiple Get Actions Are Present

我与影子孤独终老i 提交于 2019-12-03 17:04:52

I have found an elegant solution to the problem.

I modified my ApiRouteConfig to have the following routes:

        config.Routes.MapHttpRoute(
            name: "DefaultGetApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional, action = "Get" },
            constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
        );

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: new { id = @"\d+" }
        );            

        config.Routes.MapHttpRoute(
            name: "ActionApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
        );

Now I can access:

/api/exploitation
/api/exploitation/1
/api/exploitation/getbysongid/1
/api/exploitation/getbysongwriterid/1

I did not need to modify my controller actions to work with this new routing config at all.

If you had multiple PUT or POST actions you could the create new routes that looked as follows:

    config.Routes.MapHttpRoute(
        name: "DefaultGetApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Put" },
        constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Put) }
    );

    config.Routes.MapHttpRoute(
        name: "DefaultGetApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Delete" },
        constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Delete) }
    );

I hope that this answer helps everyone as this seems to be a common issue that people are having.

The problem you have is that /api/exploitation/1 falls under:

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

All of your GET methods satisfy that routing as well particularly because {id} is optional and their controller is the same.

So you have one HTTP GET request from the client and multiple methods that accept GET requests. It doesn't know which one to go to.

api/{controller}/{action}/{id} 
//This works fine because you specified which action explicitly

I hope that answers your question.

Try the following in your route definition. Keep only the following route:

  config.Routes.MapHttpRoute(
        name: "ActionApi",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Get" },
        constraints: new { id = @"\d+" }
    );

Make the first Get method private, modify the second one so that id has a default value:

// GET api/Exploitation
private HttpResponseMessage Get() {
    // implementation stays the same but now it's private
}

// GET api/Exploitation/5        
[HttpGet, ActionName("Get")]
public HttpResponseMessage Get(int id = 0) {
    if (id == 0) {
        return Get();
    }

    // continue standard implementation
}

This way (I haven't tested it myself) I expect that:

  • api/Exploitation/ will map to api/Exploitation/Get(as default action) with id = 0 as default param
  • api/Exploitation/1 will map to api/Exploitation/Get/1 so it will call Get(1)
  • api/Exploitation/someOtherAction/345 will call the correct action method

This might work. A tighter route definition could actually be like this:

  config.Routes.MapHttpRoute(
        name: "ApiWithRequiredId",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: null /* make sure we have explicit action and id */,
        constraints: new { id = @"\d+" }
    );

  config.Routes.MapHttpRoute(
        name: "ApiWithOptionalId",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Get" },
        constraints: new { action = "Get" /* only allow Get method to work with an optional id */, id = @"\d+" }
    );

But something along these lines... give it a try I hope it solves your problem.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!