【问题标题】:.net core routing, understand what application called with a generic route.net 核心路由,了解使用通用路由调用的应用程序
【发布时间】:2019-10-24 23:13:39
【问题描述】:

所以我有一个基本控制器,下面的[Route] 定义

[Route("{application}/api/[controller]")]
public class BaseController
{

}

我当前所有的控制器都继承自BaseController

我想要实现的是两个不同的应用程序可以调用我的控制器和我的代码以了解“应用程序”正在调用它。

应用程序 1 应该能够调用 /Application1/Api/MyController
应用程序 2 应该可以调用 /Application2/Api/MyController

两个请求都应该发送到同一个控制器,但我的代码应该知道是哪个应用程序调用了它。

我曾想过拥有某种中间件,然后从Request.Path 中计算出应用程序,然后将其存储在HttpContext.Current.Items 之类的东西中,但这似乎不是正确的做法。

【问题讨论】:

  • “应用程序”是什么意思?你的意思是客户端通过 HTTP 调用它?
  • 是的,所以每个客户端都可以使用他们的客户端名称进行调用,例如 google 可以调用 /Google/Api/MyController/,它与其他示例的控制器相同。它只是一个字符串,它可以是任何东西
  • 您是否宁愿通过 HTTP 标头而不是 URL 传递此类信息?或者甚至使用映射到应用程序的某种 API 密钥?
  • 我并没有真正考虑过使用 HTTP 标头。有可能,客户端通过路由更容易做到这一点
  • 我认为从语义上讲它更有意义,如果 URL 是资源的标识符,那么它应该根据调用它的应用程序而改变。

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


【解决方案1】:

我个人的偏好是将值作为 HTTP 标头而不是路由参数传递,特别是如果您希望它无处不在。这意味着您不需要 Route 属性和每个应用程序的不同 URL。使用自定义ActionFilterAttribute,您可以通过多种方式将此详细信息传递到您的操作中。例如:

public class ApplicationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.HttpContext.Request.Headers.TryGetValue("Application", out var values))
        {
            // Method 1: This allows you to specify a parameter on your action
            context.ActionArguments.Add("application", values.First());

            // Method 2: This adds the value into the route data
            context.RouteData.Values.Add("Application", values.First());

            // Method 3: This will set a property on your controller
            if (context.Controller is BaseApplicationController baseController)
            {
                baseController.Application = values.First();
            }
        }

        base.OnActionExecuting(context);
    }
}

并将其应用于操作方法或您的控制器:

[Application]
public class FooController : Controller
{
}

方法一用法:

public IActionResult Index(string application)
{
    // do something with the parameter passed in
}

方法二用法:

public IActionResult Index(string application)
{
    var application = (string)RouteData.Values["Application"];
}

方法三用法:

首先,创建一个包含该属性的基本控制器:

public abstract class BaseApplicationController : Controller
{
    public string Application { get; set; }
}

然后确保你的控制器继承自它:

[Application]
public class FooController : BaseApplicationController
{
}

现在您可以访问控制器上的属性:

public IActionResult Index(string application)
{
    var application = this.Application;
}

奖励方法4:

顺便说一句,您可以使用此方法来使用 URL 路由值,使用方法 3 中的基本控制器,将属性修改为如下所示:

public class ApplicationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.Controller is BaseApplicationController baseController)
        {
            baseController.Application = (string)context.RouteData.Values["application"];
        }

        base.OnActionExecuting(context);
    }
}

为你的控制器添加路由属性:

[Route("{application}/api/[controller]/[action]")]

现在您应该在控制器集上设置了属性值。

【讨论】:

    【解决方案2】:

    您可以将路由模板移动到动作中,然后每个动作在技术上都会通过您建议的约定知道其调用者上下文:

    [Route("api/[controller]")]
    public class YourController : BaseController
    {
        [HttpGet("{application}")]
        public IActionResult Get(string application)
        {
            if (application == "Application1")
            {
                ...Application1 called
            }
            if (application == "Application2")
            {
                ...Application2 called
            }
            ...        
        } 
    }
    

    当然,这是您建议的约定,它不会以任何方式通过某些自定义应用程序身份验证强制执行,因此您必须相信您的调用者将通过此约定正确识别自己。

    另一种方法,可能是有一个基类变量并在检查路由后设置它。

    [Route("{application}/api/[controller]")
    public class BaseController: Controller
    {
        protected string CallingApp { get; set; }
    
        public override void OnActionExecuting(ActionExecutingContext ctx)
        {
            CallingApp  = ctx.RouteData.Values["application"];
            base.OnActionExecuting(ctx);
        }
    }
    

    【讨论】:

    • 将它作为操作方法的一部分的问题是我必须把它放在每一个上。理想情况下,我希望在一个地方完成所有操作,任何人都可以添加更多 API 而不必担心调用应用程序
    • 你不能简单地检查基类构造函数中的路由并根据你的约定设置一些标识调用者上下文的字符串变量吗?
    • 您建议如何在基类构造函数中访问该路由变量?
    • @JamieRees 我已经向您添加了如何完成的示例。
    • 感谢@DavidG,我最初认为它可以在构造函数中完成,因此是只读的,但后来意识到HttpContext 在调用构造函数时将不可用。
    猜你喜欢
    • 2017-11-18
    • 2018-08-09
    • 2023-03-07
    • 1970-01-01
    • 2017-08-19
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    • 2013-10-26
    相关资源
    最近更新 更多