找到了与请求匹配的多个操作:(获取和下载)方法

时间:2016-03-12 17:07:09

标签: asp.net asp.net-web-api

我已经在这里看过几次问这个问题了,但是没有一个答案似乎适用于我的情况。

我的web api控制器上有三个GET方法,我也想在这里添加POST / PUT / DELETE方法。

  • GET#1 - 从查询字符串中读取参数的查询方法
  • GET#2 - 根据它的实体ID返回单个对象。
  • GET#3 - 下载该对象的附件,并根据其实体ID查找该对象。
  • 还需要确保路由也能够处理POST / PUT / DELETE调用。

请在此处查看控制器代码:

public PagedResult<ApiStudentNote> Get([FromUri] StudentNoteApiCriteria criteria)

public ApiStudentNote Get(long id)

[HttpGet]
[ActionName("Download")]
public HttpResponseMessage Download(long id)

我正在尝试配置路由,但仍然会收到标题中提到的错误:

context.Routes.MapHttpRoute(
    name: "V2/DownloadStudentNotes",
    routeTemplate: areaName + "/V2/student-notes/{id}/Download",
    defaults: new { controller = "StudentNotes", action = "Download" },
    constraints: new { id = @"\d+" });

context.Routes.MapHttpRoute(
    name: "V2/StudentNotesApi",
    routeTemplate: areaName + "/V2/student-notes/{id}",
    defaults: new { controller = "StudentNotes", id = RouteParameter.Optional });

2 个答案:

答案 0 :(得分:1)

如果您使用的是ASP.NET Web API 2+,则可以使用“属性路由”来解决此问题。它将为您提供更大的灵活性。

Attribute Routing in ASP.NET Web API 2

  

为什么要进行属性路由?

     

Web API的第一个版本使用基于约定的路由。在那里面   路由类型,您定义一个或多个路由模板,它们是   基本上参数化字符串。当框架收到一个   请求,它匹配路由模板的URI。

     

基于约定的路由的一个优点是模板   在一个地方定义,并应用路由规则   贯穿所有控制器。不幸的是,基于会议   路由使得很难支持常见的某些URI模式   在RESTful API中。例如,资源通常包含子资源:   客户有订单,电影有演员,书有作者等等   向前。创建反映这些关系的URI是很自然的:

     

/客户/ 1 /订单

     

这种类型的URI很难使用基于约定的方法创建   路由。虽然可以做到,但如果你这样做,结果就不能很好地扩展   有许多控制器或资源类型。

     

使用属性路由,为此URI定义路由非常简单。   您只需向控制器操作添加一个属性:

配置路由

public static class WebApiConfig {
    public static void Register(HttpConfiguration config) {
        // Web API routes
        config.MapHttpAttributeRoutes();

        // Other Web API configuration not shown.
    }
}

控制器代码:

[RoutePrefix("api/v2/student-notes")]
public class StudentNotesController : ApiController {

    // GET api/v2/student-notes
    [HttpGet]
    [Route("")]
    public PagedResult<ApiStudentNote> GetNotes([FromUri] StudentNoteApiCriteria criteria) {...}

    // GET api/v2/student-notes/1234567
    [HttpGet]
    [Route("{id:long}")]
    public ApiStudentNote GetNote(long id) {...}

    // GET api/v2/student-notes/1234567/Download
    [HttpGet]
    [Route("{id:long}/Download")]
    public HttpResponseMessage Download(long id) {...}

}

因为在模板

中添加了Route Constraints
  

API / V2 /学生笔记/ {ID}

以下URI与此模板匹配:

  • api/v2/student-notes/1234其中id =&gt; 1234
  • api/v2/student-notes/1234/Download其中id =&gt; 1234 /下载

这将反映您最初与基于会议的路线的多个匹配。

答案 1 :(得分:0)

我认为您使用的是Web API 2或更高版本。如果是这样,您在WebApiConfig中不需要任何基于约定的路由。您只需启用属性路由并装饰您的方法:

public static class WebApiConfig 
{
    public static void Register(HttpConfiguration config) {
        config.MapHttpAttributeRoutes();

        // No route config needed beyond this point.

    }
}

[RoutePrefix("api/v2/student-notes")]
public class StudentNotesController : ApiController 
{
    public PagedResult<ApiStudentNote> GetNotes(
        [FromUri] StudentNoteApiCriteria criteria)
    {
        //...
    }

    [Route("{id}", Name="GetNote")] 
    public ApiStudentNote GetNote(long id)
    {
        //...
    }

    [Route("{id}/Download")]
    public HttpResponseMessage Download(long id)
    {
        //...
    }

    // in future scenarios this will work as well
    public ApiStudentNote PostNote(ApiStudentNote studentNote)
    {
        // persist new student note
        return CreatedAt("GetNote", new {id = studentNote.Id }, studentNote);
    }

    [Route("{id}")]
    public ApiStudentNote PutNote(ApiStudentNote studentNote)
    {
        // retrieve
        // update
        // persist
        return Ok();
    }
}

正如您所看到的,我没有用HttpGet等来装饰方法,因为路由服务可以从方法名称推断出HTTP方法(GetNote推断GET,{{ 1}}推断PostNote)。为清楚起见,您可以添加属性。

关于POST方法的命名路线。我补充说,为了向您展示您在未来场景中如何实现GetNote以及如何在POST标题中返回新创建的注释的位置。如果您提供路线名称,Location可以为您做到这一点。