MVC API路由当多个GET操作是否存在,(MVC API Routing When Multip

2019-08-17 10:16发布

似乎有一千人询问有关堆栈溢出同样的问题,但似乎没有成为一个单一的解决这个问题。 我要再问吧...

我有一个具有以下操作的API控制:

    // GET api/Exploitation
    public HttpResponseMessage Get() {
        var items = _exploitationRepository.FindAll();

        var mappedItems = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(items);

        var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItems);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { }));
        return response;
    }

    // GET api/Exploitation/5        
    [HttpGet, ActionName("Get")]
    public HttpResponseMessage Get(int id) {
        var item = _exploitationRepository.FindById(id);
        var mappedItem = Mapper.Map<Exploitation, ExploitationView>(item);

        var response = Request.CreateResponse<ExploitationView>(HttpStatusCode.OK, mappedItem);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
        return response;
    }

    // GET api/Exploitation/GetBySongwriterId/5
    [HttpGet, ActionName("GetBySongwriterId")]
    public HttpResponseMessage GetBySongwriterId(int id) {
        var item = _exploitationRepository.Find(e => e.Song.SongWriterSongs.Any(s => s.SongWriterId == id))
                                          .OrderByDescending(e => e.ReleaseDate);
        var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);

        var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
        return response;
    }

    // GET api/Exploitation/GetBySongwriterId/5
    [HttpGet, ActionName("GetBySongId")]
    public HttpResponseMessage GetBySongId(int id) {
        var item = _exploitationRepository.Find(e => e.SongId == id)
                                          .OrderByDescending(e => e.ReleaseDate);
        var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);

        var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
        return response;
    }

在我APIConfig我已经定义了以下路线:

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

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

我发现,我可以访问以下操作没有问题:/ API /开发/ API /开发/ getbysongwriterid / 1 / API /开发/ getbysongid / 1

当我尝试访问/ API /开发/ 1,我得到这个异常

"Multiple actions were found that match the request: System.Net.Http.HttpResponseMessage Get(Int32) on type Songistry.API.ExploitationController System.Net.Http.HttpResponseMessage GetBySongwriterId(Int32)" exception.

任何人都可以看到什么是错我的路线? 还是错别的东西?

Answer 1:

我发现一个优雅的解决问题的办法。

我修改了ApiRouteConfig有以下途径:

        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 }
        );

现在,我可以访问:

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

我并不需要修改我的控制器行动,在所有使用新的路由配置工作。

如果你有多个PUT或POST操作,你可以在创建看起来如下新航线:

    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) }
    );

我希望这个答案可以帮助大家,因为这似乎是人有一个共同的问题。



Answer 2:

你的问题是,/ API /开发/ 1瀑布下:

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

您的所有GET方法能满足这种路由以及特别是因为{ID}是可选的,他们的控制器是一样的。

所以,你必须从客户端和接受GET请求多种方法中的一个HTTP GET请求。 它不知道去哪个。

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

我希望这能回答你的问题。



Answer 3:

尝试在你的路由定义如下。 只保留了以下路线:

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

做第一个GET方法私有的,修改的第二个使ID有默认值:

// 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
}

这样,(我还没有测试它自己)我想到的是:

  • API /开发/将映射到API /开发/获取(如默认操作)使用id = 0作为默认PARAM
  • API /开发/ 1将映射到API /开发/获得/ 1,因此它会调用获取(1)
  • API /开发/ someOtherAction / 345将调用正确的操作方法

这可能会实现。 更紧密路由定义实际上可能是这样的:

  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+" }
    );

但这些方针的东西......试试看我希望它可以解决你的问题。



文章来源: MVC API Routing When Multiple Get Actions Are Present