【问题标题】:Asp.Core 2.1 [ApiController] attributeAsp.Core 2.1 [ApiController] 属性
【发布时间】:2019-06-17 10:48:06
【问题描述】:

我有一段时间没有写 api 了(最后一次是在 asp.core 2.0 中)所以今天决定在 .core 2.1 版本中创建一个新的,不幸的是发现我的操作不像以前那样工作了以前的版本。

几个小时后,我发现这个问题导致[Route("api/[controller]")] 和 since[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})";
        }
    }

` 启动.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. 好的
  2. 好的
  3. id=0
  4. 好的
  5. 404
  6. 404

5&6 现在是一样的

【问题讨论】:

  • 对于#3,您告诉路由器将路由模式中的 {id} 绑定到方法参数,然后告诉方法参数绑定到 [FromQuery]...似乎 [FromQuery] 获胜(您在 #3 url 中没有 ?id=1 )。 #4 和 #5 的 URL 与路由路径不匹配(URL 以 A4/1 结尾,但路由显示“A4”...您可能打算拥有 Route("A4/{id}") 然后删除它[FromQuery] 在方法签名中)

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


【解决方案1】:

[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 的预期路线。

【讨论】:

  • 感谢您的回答和详细解释。不幸的是,如果控制器具有[ApiController] 属性,则每个操作都应该定义路由,否则会出现 InvalidOperationException 错误。我根据您在更新问题中可能看到的答案更改对我的代码进行了一些更改,但仍有 3/6 个 URL 不起作用。
【解决方案2】:

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

[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 作为示例

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2021-03-08
      • 2018-11-27
      • 2021-06-07
      • 1970-01-01
      • 1970-01-01
      • 2014-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多