2013-02-27 64 views
3

我已经阅读了很多关于路由和控制器的问题,但我根本找不到我在找什么。我有这种结构的控制器:找到了与请求匹配的多个操作

更新:包括完整的源代码。

public class LocationsController : ApiController 
{ 
    private readonly IUnitOfWork _unitOfWork; 

    public LocationsController(IUnitOfWork unitOfWork) 
    { 
     _unitOfWork = unitOfWork; 
    } 

    // GET /api/locations/id 
    public Location Get(Guid id) 
    { 
     return this.QueryById<Location>(id, _unitOfWork); 
    } 

    // GET /api/locations 
    public IQueryable<Location> Get() 
    { 
     return this.Query<Location>(_unitOfWork); 
    } 

    // POST /api/locations 
    public HttpResponseMessage Post(Location location) 
    { 
     var id = _unitOfWork.CurrentSession.Save(location); 
     _unitOfWork.Commit(); 

     var response = Request.CreateResponse<Location>(HttpStatusCode.Created, location); 
     response.Headers.Location = new Uri(Request.RequestUri, Url.Route(null, new { id })); 

     return response; 
    } 

    // PUT /api/locations 
    public Location Put(Location location) 
    { 
     var existingLocation = _unitOfWork.CurrentSession.Query<Location>().SingleOrDefault(x => x.Id == location.Id); 

     //check to ensure update can occur 
     if (existingLocation == null) 
     { 
      throw new HttpResponseException(HttpStatusCode.NotFound); 
     } 
     //merge detached entity into session 
     _unitOfWork.CurrentSession.Merge(location); 
     _unitOfWork.Commit(); 

     return location; 
    } 

    // DELETE /api/locations/5 
    public HttpResponseMessage Delete(Guid id) 
    { 
     var existingLocation = _unitOfWork.CurrentSession.Query<Location>().SingleOrDefault(x => x.Id == id); 

     //check to ensure delete can occur 
     if (existingLocation != null) 
     { 
      _unitOfWork.CurrentSession.Delete(existingLocation); 
      _unitOfWork.Commit(); 
     } 

     return new HttpResponseMessage(HttpStatusCode.NoContent); 
    } 

    // rpc/locations 
    public HttpResponseMessage Dummy() 
    { 
     // I use it to generate some random data to fill the database in a easy fashion 
     Location location = new Location(); 
     location.Latitude = RandomData.Number.GetRandomDouble(-90, 90); 
     location.Longitude = RandomData.Number.GetRandomDouble(-180, 180); 
     location.Name = RandomData.LoremIpsum.GetSentence(4, false); 

     var id = _unitOfWork.CurrentSession.Save(location); 
     _unitOfWork.Commit(); 

     var response = Request.CreateResponse<Location>(HttpStatusCode.Created, location); 
     response.Headers.Location = new Uri(Request.RequestUri, Url.Route(null, new { id })); 

     return response; 
    } 
} 

我的路线定义(Global.asax中):

public static void RegisterRoutes(RouteCollection routes) 
{ 
    // Default route 
    routes.MapHttpRoute(
     name: "Default", 
     routeTemplate: "{controller}/{id}", 
     defaults: new { id = RouteParameter.Optional } 
    ); 

    // A route that enables RPC requests 
    routes.MapHttpRoute(
     name: "RpcApi", 
     routeTemplate: "rpc/{controller}/{action}", 
     defaults: new { action = "Get" } 
    ); 
} 

到目前为止,如果我打的浏览器:

  • [baseaddress]/locations/s0m3-gu1d-g0e5-hee5eeeee //它的工作原理
  • [baseaddress]/locations/ //找到多个结果
  • [baseaddress]/rpc/locations/dummy //它的工作原理

最奇怪的是,这曾经工作,直到我搞砸了我的NuGet在执行一些更新。我在这里错过了什么?

以GET,POST,PUT或delete开头的动词将自动映射到第一个路线,我的虚拟测试方法将通过rpc调用,这将落入第二个路线。

时引发的误差是InvalidOperationException与消息

多个动作被发现匹配的要求,即:上式MYPROJECT System.Linq.IQueryable`1 [Myproject.Domain.Location]取得()。 Webservices.Controllers.LocationsController System.Net.Http.HttpResponseMessage假人()的类型Myproject.Webservices.Controllers.LocationsController

任何想法?

+0

请您可以发布LocationsController的完整代码,包括修剪的RPC方法吗? – 2013-02-27 15:18:34

+0

_“,这曾经工作,直到我搞砸了我的NuGet,而执行一些更新。我在这里错过了什么?”_ - 源代码管理。右键点击 - >撤消/还原。 – CodeCaster 2013-02-27 15:23:27

+0

该项目建成并运行良好。我宁愿尝试修复并了解什么是错误的,而不是仅仅恢复。(我承认这种回复会让我回到几个小时)。 – Joel 2013-02-27 15:28:29

回答

5

问题出在路由加载的顺序。如果他们是这样的:

// A route that enables RPC requests 
routes.MapHttpRoute(
    name: "RpcApi", 
    routeTemplate: "rpc/{controller}/{action}", 
    defaults: new { action = "Get" } 
); 

// Default route 
routes.MapHttpRoute(
    name: "Default", 
    routeTemplate: "{controller}/{id}", 
    defaults: new { id = RouteParameter.Optional } 
); 

它会正常工作。首先,请求将映射到RPC,然后映射到控制器(它可能有或没有Id)。

+0

“它会正常工作” 它确实做到了!感谢你让我在正确的方向! 在旁注中,是否需要新路线? (我问,因为只有前2个工作,只要我重新排序) – Joel 2013-02-27 15:40:14

+0

@Joel:我刚刚测试,似乎并不需要。如果增加一个路由,将会使你很容易中断 – 2013-02-27 15:52:54

+0

我最初的想法是让REST处理4个Http动词并仅在真正需要时才使用RPC调用。我建议你编辑你的答案,并删除不必要的路线:) – Joel 2013-02-27 16:27:49

0

我们也可以创建阿比控制器自定义操作选择如下,以便能够自由地与复杂类型的工作传统的“GET,POST,PUT,DELETE”:

class ApiActionSelector : IHttpActionSelector 
    { 
     private readonly IHttpActionSelector defaultSelector; 

     public ApiActionSelector(IHttpActionSelector defaultSelector) 
     { 
      this.defaultSelector = defaultSelector; 
     } 
     public ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor) 
     { 
      return defaultSelector.GetActionMapping(controllerDescriptor); 
     } 
     public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext) 
     { 
      // Get HttpMethod from current context 
      HttpMethod httpMethod = controllerContext.Request.Method; 

      // Set route values for API 
      controllerContext.RouteData.Values.Add("action", httpMethod.Method); 

      // Invoke Action 
      return defaultSelector.SelectAction(controllerContext); 
     } 
    } 

我们可以注册在WebApiConfig中也是这样:

config.Services.Replace(typeof(IHttpActionSelector), new 
ApiActionSelector(config.Services.GetActionSelector())); 

可以帮助运行这类问题的用户。

相关问题