Web API中批量OData请求的HttpContext为NULL -错误的设计模式

本文关键字:HttpContext NULL 设计模式 错误 请求 API OData Web | 更新日期: 2023-09-27 18:15:19

我正在尝试确定正确的方法来注入一个依赖到一个控制器,其中要注入的具体类型是一个基于路由数据参数的变量。

到目前为止,我有以下设置,可以完美地用于正常请求:

控制器

 public class OrdersController : ODataController
 {
    private IOrderService ErpService { get; }
    public OrdersController(IOrderService orderService)
    {
        ErpService = orderService;
    }
    [EnableQuery(PageSize = 100)]
    public IQueryable<OrderDto> Get(ODataQueryOptions<OrderDto> queryOptions)
    {
        return ErpService.Orders(queryOptions);
    }
    ...
    // Post
    // Patch/Put
    // Delete
}

使用下面的OData路由配置,我可以指定路由模板应该包含一个'company'参数:

配置

config.MapODataServiceRoute( "ODataRoute", "data/{company}", model, new DefaultODataPathHandler(),
                conventions, new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));

这允许我有一个静态方法从URL读取公司ID:

public static string GetSalesCompanyFromRequest()
{
    var salesCompany = "";
    if (HttpContext.Current == null) return "";
    var routeData = HttpContext.Current.Request.RequestContext.RouteData;
    if (routeData.Values.ContainsKey("company"))
    {
        salesCompany = routeData.Values["company"].ToString();
    }
    return salesCompany;
}

然后,使用Ninject,我可以选择使用哪个IOrderService的具体实例(为了简洁而简化):

kernel.Bind<IOrderService>()
            .To<SageOrderService>()
            .When(ctx => GetSalesCompanyFromRequest() == "101").InRequestScope();
kernel.Bind<IOrderService>()
            .To<DynamicsAxOrderService>()
            .When(ctx => GetSalesCompanyFromRequest() == "222").InRequestScope();
kernel.Bind<IOrderService>()
            .To<SapOrderService>()
            .When(ctx => GetSalesCompanyFromRequest() == "333").InRequestScope();

连接器配置

Id          ErpType        ConnectionString
--------------------------------------------
111         Sage           "connectionstring1"
222         DynamicsAx     "connectionstring2"
333         SAP            "connectionstring3"

下面是如何处理以下url的:

http://odata-demo/data/101/Orders
创建SageOrderService并将其注入OrdersController

http://odata-demo/data/ 222 /
订单创建DynamicsAxOrderService并注入OrdersController

相同的逻辑适用于许多不同的服务,如:

  • SageStockService/AxStockService
  • SageBomService/AxBomService

注意:我选择将公司Id放在URL中,这样我就可以配置反向代理将请求转发到更靠近目标数据库的本地web服务器。

这一切工作完美,直到我尝试使用OData批处理。当我发送批量请求时,似乎没有HttpContext.Current(它是null)。

这个问题问了类似的问题,但没有考虑到OData批处理请求。

这个回答中的注释表明,通过路由数据注入有代码味道,但没有详细说明。

所以,问题是,我如何获得HttpContext.Current批处理OData请求?或者有没有更好的方法来做我想做的事?

Web API中批量OData请求的HttpContext为NULL -错误的设计模式

由于公司已经包含在路由数据中,我可以为每个动作添加一个额外的公司参数,如下所示,以允许传入公司编号,然后使用工厂来获得正确的具体类型:

public class OrdersController : ODataController
{
    [EnableQuery(PageSize = 100)]
    public IQueryable<OrderDto> Get(ODataQueryOptions<OrderDto> queryOptions, string company)
    {
        var erpService = ErpServiceFactory.GetService(company);
        return erpService.Orders(queryOptions);
    }
    ...
    // Post
    // Patch/Put
    // Delete
}

这意味着我还必须在每个动作中初始化OrderService,这有点难闻。

我想如果我使用ActionFilter来定位并传递正确的具体类型给动作,这可能会不那么糟糕:

public class RequiresOrderServiceAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        string salesCompany = "";
        var data = actionContext.Request.GetRouteData();
        if (data.Values.ContainsKey("company"))
        {
            salesCompany = data.Values["company"].ToString();
            var orderService = ErpServiceFactory.GetService(company);
            actionContext.ActionArguments.Add("erpService", orderService);
        }
    }
}
public class OrdersController : ODataController
{
    [EnableQuery(PageSize = 100)]
    public IQueryable<OrderDto> Get(ODataQueryOptions<OrderDto> queryOptions, IOrderService erpService)
    {
        return erpService.Orders(queryOptions);
    }
    ...
    // Post
    // Patch/Put
    // Delete
}

想法吗?