Asp.Core 2.1 [ApiController]属性

时间:2019-01-23 19:11:52

标签: asp.net-core asp.net-core-webapi asp.net-core-2.1

我已经有一段时间没有写过api了(上次是在asp.core 2.0中),所以今天决定在.core 2.1版本中创建一个新的api,不幸的是发现我的Action不能像以前那样在previos版本。

几个小时后,我找出了导致[Route("api/[controller]")]的问题,并且由于[ApiController]无法解决问题,我对它们都进行了评论,并且一切正常。 **所以有人可以向我解释一下如何使此代码与[[ApiController]] unconnebt和相同的操作URL调用一起使用吗?

   //[Route("api/[controller]")]
   //[ApiController]
    public class TestController : ControllerBase
    {
        [HttpGet("api/[controller]")]
        public string A1()
        {
            return "A1()";
        }

        [HttpGet]
        public string A2(int id)
        {
            return $"A2(int {id})";
        }

        [HttpGet]
        public string A3(string p1,string p2)
        {
            return $"A3(string {p1},string {p2})";
        }
        [Route("api/[controller]/A4/{id}")]
        [HttpGet]
        public string A4(int id)
        {
            return $"A4(int {id})";
        }
        [HttpGet("api/[controller]/A5/{id}")]
        public string A5(int id)
        {
            return $"A5(int {id})";
        }
    }

` StartUp.css

   public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{……///code
      app.UseMvc(routes =>
                {
                    routes.MapRoute(
                        name: "default",
                        template: "api/{controller=Home}/{action=Index}/{id?}");
                });
    }
  1. https://localhost:5001/api/Test
  2. https://localhost:5001/api/Test/A2?id=1
  3. https://localhost:5001/api/Test/A2/1
  4. https://localhost:5001/api/Test/A3?p1=test&p2=test
  5. https://localhost:5001/api/Test/A4/1
  6. https://localhost:5001/api/Test/a5/1

测试1 : 我根据@ chris-pratt答案进行了更改

   [Route("api/[controller]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        [HttpGet("")]
        public string A1()
        {
            return "A1()";
        }

        [HttpGet("A2")]
        [HttpGet("A2/{id}")]
        public string A2([FromQuery]int id)
        {
            return $"A2(int {id})";
        }

        [HttpGet("A3")]
        public string A3([FromQuery]string p1, [FromQuery]string p2)
        {
            return $"A3(string {p1},string {p2})";
        }


        [HttpGet("A4")]
        public string A4([FromQuery]int id)
        {
            return $"A4(int {id})";
        }

        [HttpGet("A5")]
        public string A5([FromQuery]int id)
        {
            return $"A5(int {id})";
        }
    }

网址调用:

  1. id = 0
  2. 404
  3. 404

5&6现在相同

3 个答案:

答案 0 :(得分:1)

对于5&6,您不需要使用[FromQuery]。对于2&3,如果您仍然希望同时使用查询字符串和属性路由,则需要将SuppressInferBindingSourcesForParameters属性设置为true以禁用默认推理规则。请参阅Binding source parameter inference

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

services.Configure<ApiBehaviorOptions>(options =>
{ 
   options.SuppressInferBindingSourcesForParameters = true;
});

网络api控制器:

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet("")]
public string A1()
{
    return "A1()";
}

[HttpGet("A2")]
[HttpGet("A2/{id}")]
public string A2(int id)
{
    return $"A2(int {id})";
}

[HttpGet("A3")]
public string A3([FromQuery]string p1, [FromQuery]string p2)
{
    return $"A3(string {p1},string {p2})";
}


[HttpGet("A4/{id}")]
public string A4(int id)
{
    return $"A4(int {id})";
}

[HttpGet("A5/{id}")]
public string A5(int id)
{
    return $"A5(int {id})";
}
}

答案 1 :(得分:0)

[Route("api/[controller]")][ApiController]都不是您的实际问题。控制器类级别的[Route]属性为该控制器中的所有操作指定路由前缀。起飞时,您将回到MVC中的默认路由/{controller}/{action}/{id?}

认识到使用属性路由在其应用的级别覆盖默认路由 也很重要。换句话说,如果您未将Route应用于控制器,则那里的操作将使用默认路由。如果将Route属性或诸如HttpGet之类的HTTP方法属性之一应用于操作,则仅单个操作将使用自定义属性路由。但是,如果确实将Route属性应用于控制器,则即使您未显式应用属性,控制器中的所有操作都将使用属性路由。因此,为每个操作赋予其自己的独特路线非常重要。

您的第一个问题是,您对某些操作所应用的路径与对控制器所应用的相同。最终结果是该操作的路由最终实际上是/api/test/api/test。您只需要指定路由中前缀之后的部分,即[HttpGet("a1")]。如果您希望它只是前缀,而没有自己的附加路由段,则只需使用空路由,即[HttpGet("")]或仅使用[HttpGet]。只需确保每个HTTP方法只执行一次即可。

现在与应用[ApiController]的区别在于,除其他外,它将默认绑定从FromForm切换到FromBody。但是,这仅适用于引用类型的参数,例如类。像字符串这样的值类型将是未绑定的。显然,由于您想从查询字符串中获取这些内容,因此应在其中添加[FromQuery]

    [HttpGet]
    public string A3([FromQuery]string p1, [FromQuery]string p2)

最后,您还会有一堆重复的路线。正如我在上面概述的那样,未指定路由与在操作中指定空路由相同,这意味着您所拥有的只是控制器上设置的路由前缀。您需要确保每个操作都有其响应的唯一路由。例如,上面的方法实际上应该具有类似[HttpGet("a3")]之类的东西,它会为您提供/api/test/a3?p1=test&p2=test的预期路线。

答案 2 :(得分:0)

我对您的代码进行了一些更改,请参阅我建议的解决方案。现在已经设置了路由,控制器上的每个方法都将作为路由名称形成,并且参数上的[FromRoute]标签表明它们将来自HTTP Verb下指定的路由。

[Route("api/[controller]/[action]")]
public class TestController : Controller
{
    public string A1()
    {
        return "A1()";
    }

    [HttpGet]
    [Route("{id}")]
    public string A2([FromRoute]int id)
    {
        return $"A2(int {id})";
    }

    [HttpGet]
    [Route("{p1}/{p2}")]
    public string A3([FromRoute]string p1,[FromRoute]string p2)
    {
        return $"A3(string {p1},string {p2})";
    }

    [HttpGet]
    [Route("{id}")]
    public string A4([FromRoute]int id)
    {
        return $"A4(int {id})";
    }
    [HttpGet("{id}")]
    public string A5([FromRoute]int id)
    {
        return $"A5(int {id})";
    }
}

您的路线现在应类似于http://localhost:5200/api/test/A2/5