【问题标题】:How to set different routes for the same Controller method with different parameters?同一个Controller方法,不同参数如何设置不同的路由?
【发布时间】:2018-01-04 04:46:07
【问题描述】:

我的 ReportController 看起来像这样:

public IActionResult ReportDetails(int? reportId){
    ....
}

public IActionResult ReportDetails(int? reportId, bool ? approved) {
    ....
}

我试图做一些路由,但它失败了。 Startup.cs 中编写的实际代码:

app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "ReportDetailsValidation",
                    template: "descriptionValidation",
                    defaults: new { controller = "Report", action = "ReportDetails", reportId ="{reportId}", approved = "{approved}" }
                    );

                routes.MapRoute(
                    name: "ReportDetails",
                    template: "description",
                    defaults: new { controller = "Report", action = "ReportDetails"} 
                    );

                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

正如我所见,我需要匹配以下网址:
/Report/ReportDetails?reportId=7 作为第一个,
/Report/ReportDetails?reportId= 7&approved=True 第二次

【问题讨论】:

    标签: c# asp.net asp.net-core routing


    【解决方案1】:

    你可以使用 RouteAttribute

    [Route("ReportDetails/{reportId}")]
    public IActionResult ReportDetails(int? reportId){
    ....
    [Route("ReportDetails/{reportId}/{approved}")]
    public IActionResult ReportDetails(int? reportId, bool ? approved) {
    ....
    

    您的网址将是;

    /Report/ReportDetails/7 为第一和

    /Report/ReportDetails/7/第二次为真

    这将适用于默认的 startup.cs

    app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
    

    【讨论】:

    • 是的,我想过这个,但正如我提到的,我的链接是我指定的。我在视图中使用了 @Html.ActionLinkFor (我不明白为什么它使 url 在视图名称后没有“/”)。我希望链接与您的相同
    【解决方案2】:

    你不能这样做,比如有 2 个具有相同名称但参数不同的方法,因为你应该得到 AmbiguousActionException

    ASP 核心动作选择器在选择动作期间不关心动作的参数。因此,您不能让 2 个操作对应一个路由模板。唯一的解决方法是使用操作约束(如 HttpGet 和 HttpPost)。 你可以check it yourself on GitHub。 (参见SelectBestCandidate方法)

    理论上,如果参数很重要,基于参数在动作之间选择的逻辑应该在 SelectBestActions 方法中,但它会忽略参数。

    在你的情况下,好的选择似乎只有一种方法:

    public IActionResult ReportDetails(int? reportId, bool? approved) {
        ....
    }
    

    然后检查是否approved为NULL(/Report/ReportDetails?reportId=7),然后执行第一个逻辑,否则(/Report/ReportDetails?reportId=7&approved=True) 实现第二个逻辑。如果逻辑包含很多行,您可以在新方法中提取它们并调用它。

    public IActionResult ReportDetails(int? reportId, bool? approved) {
         if (approved != null)
         {
            //implement logic for 2nd case(approved=True)
         }
         //implement logic for 1st case (just reportId=7)
    }
    

    【讨论】:

    • 在标准情况下你会得到上面提到的异常,但是拥有多个同名的方法完全没有问题。您只需要使用ActionMethodSelectorAttributes 来帮助选择过程(请参阅我的回答)。但是在此处介绍的特定情况下,您是对的,没有必要,使用默认参数更容易。
    【解决方案3】:

    您可以创建一个ActionMethodSelectorAttribute。在您的控制器中,您可以将属性添加到这两种方法中,以使正确的方法与 URL 匹配。

    例如,您可以创建一个ParameterExistsAttribute 来确定该方法是否合适,然后使用

    [ParameterExists('approved', false)]
    public IActionResult ReportDetails(int? reportId){
        ....
    }
    
    [ParameterExists('approved', true)]
    public IActionResult ReportDetails(int? reportId, bool ? approved) {
        ....
    }
    

    关于如何实现这样一个属性的难题仍然是读者的练习。 (没那么难。)

    作为示例,这里有一个基于参数值选择 API 调用的属性。

    public class FormPostFilterAttribute : ActionMethodSelectorAttribute
    {
      private readonly string _elementId;
      private readonly string _requiredValue;
    
      public FormPostFilterAttribute(string elementId, string requiredValue)
      {
        _elementId = elementId;
        _requiredValue = requiredValue;
      }
    
      public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
      {
        if (string.IsNullOrEmpty(controllerContext.HttpContext.Request.Form[_elementId]))
        {
          return false;
        }
    
        return (controllerContext.HttpContext.Request.Form[_elementId] == _requiredValue);
      }
    }
    

    【讨论】:

      【解决方案4】:

      你可以关注这个link了解路由

      [HttpGet("Report/ReportDetails/{reportId}")] 
      [HttpGet("Report/ReportDetails/{reportId}/{approved}")]
      public IActionResult ReportDetails(int? reportId, bool? approved) {
      ....
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-24
        • 1970-01-01
        • 2020-10-17
        相关资源
        最近更新 更多