找到了与请求匹配的多个操作

时间:2013-02-27 15:15:43

标签: c# asp.net-web-api asp.net-mvc-routing

我已经阅读了很多关于路由和控制器的问题,但我根本无法找到我正在寻找的东西。我有这个结构的控制器:

更新:包含全班来源。

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,带有消息

  

找到与请求匹配的多个操作:   System.Linq.IQueryable`1 [Myproject.Domain.Location]类型Myproject.Webservices.Controllers.LocationsController上的Get()   System.Net.Http.HttpResponseMessage类型为Myproject.Webservices.Controllers.LocationsController的Dummy()

有什么想法吗?

2 个答案:

答案 0 :(得分: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)。

答案 1 :(得分:0)

我们还可以为Api控制器创建自定义动作选择器,如下所示,这样就可以使用传统的&#34; GET,POST,PUT,DELETE&#34;自由地处理复杂类型:

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

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