OData WebApi Url中的传递参数

百乐

使用Web Api,我有一个OData EndPoint,可以从数据库返回产品。

我有多个具有相似架构的数据库,并希望在URL中传递参数以标识Api应该使用哪个数据库。

当前的Odata端点:
http:// localhost:62999 /产品

我想:
HTTP://本地主机:62999/ 999产品/产品

在新的网址中,我传入999(数据库ID)。

数据库ID旨在指定从哪个数据库加载产品。例如localhost:62999/999/Products('ABC123'),将从数据库999加载产品“ ABC123”,但下一个请求localhost:62999/111/Products('XYZ789')将从数据库111加载产品“ XYZ789”。

下面的网址有效,但我不喜欢它。
localhost:62999/Products('XYZ789')?database=111

这是控制器的代码:

public class ProductsController : ErpApiController //extends ODataController, handles disposing of database resources
{
    public ProductsController(IErpService erpService) : base(erpService) { }

    [EnableQuery(PageSize = 50)]
    public IQueryable<ProductDto> Get(ODataQueryOptions<ProductDto> queryOptions)
    {
        return ErpService.Products(queryOptions);
    }

    [EnableQuery]
    public SingleResult<ProductDto> Get([FromODataUri] string key, ODataQueryOptions<ProductDto> queryOptions)
    {
        var result = ErpService.Products(queryOptions).Where(p => p.StockCode == key);
        return SingleResult.Create(result);
    }               
}

我使用Ninject通过绑定到服务提供者来解决将IErpService的哪种实现注入到控制器中:

kernel.Bind<IErpService>().ToProvider(new ErpServiceProvider());然后ErpServiceProvider检查URL以标识此请求所需的databaseId:

public class ErpServiceProvider : Provider<IErpService>
{
    protected override IErpService CreateInstance(IContext context)
    {
        var databaseId = HttpContext.Current.Request["database"];

        return new SageErpService(new SageContext(GetDbConnection(databaseId)));
    }
}

我遇到的问题是如何在OData路由配置中定义Url参数。

正常的WebApi路由可以具有如下定义的参数:

config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
      );

但是,如何在OData路由配置中定义参数?

ODataModelBuilder builder = new ODataConventionModelBuilder();
        builder.EntitySet<ProductDto>("Products");
        builder.EntitySet<WorkOrderDto>("WorkOrders");
        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: null,
            model: builder.GetEdmModel());

这是我应该定义Url参数的地方吗?我也曾考虑过使用消息处理程序,但也不确定如何实现。

更新
此问题正试图与我做同样的事情:如何在OData上将参数声明为前缀,
但尚不清楚如何从url中读取参数。
var databaseId = HttpContext.Current.Request["database"];当前返回null。
即使将路由配置更新为以下内容:

public static void Register(HttpConfiguration config)
{
    // Web API routes
    config.MapHttpAttributeRoutes();

    config.Routes.MapHttpRoute(
        name: "ErpApi",
        routeTemplate: "{database}/{controller}"                
    );

    // Web API configuration and services
    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<ProductDto>("Products");
    builder.EntitySet<WorkOrderDto>("WorkOrders");
    config.MapODataServiceRoute(
        routeName: "ODataRoute",
        routePrefix: "{company}/",
        model: builder.GetEdmModel());

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}
JoãoAntunes

我遇到了一种在OData上传递动态参数的解决方案,不确定是否正确。

我在某种情况下使用了该解决方案,其中动态参数仅用于验证客户端,但是我认为您可以通过类似的方式解决问题。

问题:您不会在URL请求示例中传递动态值:http:// localhost: 62999 / {dynamicValue} / Products('ABC123'),但是ODataRouting将永远无法正确路由,因为该额外的/ {动态值}和ODataControler“不会命中”。使用ApiController可以进行自定义路由,但是在OData上则不能(至少我没有找到一种简单的方法,可能您必须自己制作或扩展OData路由约定)。

因此,作为替代解决方案:如果每个请求都具有一个dynamicValue,例如:“ http:// localhost: 62999 / {dynamicValue} / Products”,请执行以下步骤:

  1. 在路由请求之前,提取dynamicValue(在我的情况下,由于参数与授权相关,因此我使用了IAuthenticationFilter来拦截消息,因为该参数与授权相关,但对于您而言,使用另一种方法更有意义)
  2. 存储dynamicValue(在请求上下文中的某个位置)
  3. 路由不带{dynamicValue}的ODataController。/ Products('ABC123')代替/ {dynamicValue} / Products('ABC123')

这是代码:

// Register the ServiceRoute
public static void Register(HttpConfiguration config)
{

  // Register the filter that will intercept the request before it is rooted to OData
  config.Filters.Add(CustomAuthenticationFilter>()); // If your dynamic parameter is related with Authentication use an IAuthenticationFilter otherwise you can register a MessageHandler for example.

  // Create the default collection of built-in conventions.
  var conventions = ODataRoutingConventions.CreateDefault();

  config.MapODataServiceRoute(
          routeName: "NameOfYourRoute",
          routePrefix: null, // Here you can define a prefix if you want
          model: GetEdmModel(), //Get the model
          pathHandler: new CustomPathHandler(), //Using CustomPath to handle dynamic parameter
          routingConventions: conventions); //Use the default routing conventions
}

// Just a filter to intercept the message before it hits the controller and to extract & store the DynamicValue
public class CustomAuthenticationFilter : IAuthenticationFilter, IFilter
{
   // Extract the dynamic value
   var dynamicValueStr = ((string)context.ActionContext.RequestContext.RouteData.Values["odatapath"])
        .Substring(0, ((string)context.ActionContext.RequestContext.RouteData.Values["odatapath"])
        .IndexOf('/')); // You can use a more "safer" way to parse

   int dynamicValue;
   if (int.TryParse(dynamicValueStr, out dynamicValue))
   {
      // TODO (this I leave it to you :))
      // Store it somewhere, probably at the request "context"
      // For example as claim
   } 
}

// Define your custom path handler
public class CustomPathHandler : DefaultODataPathHandler
{
    public override ODataPath Parse(IEdmModel model, string serviceRoot, string odataPath)
    {
        // Code made to remove the "dynamicValue"
        // This is assuming the dynamicValue is on the first "/"
        int dynamicValueIndex= odataPath.IndexOf('/');
        odataPath = odataPath.Substring(dynamicValueIndex + 1);

        // Now OData will route the request normaly since the route will only have "/Products('ABC123')"
        return base.Parse(model, serviceRoot, odataPath);
    }
}

现在,您应该将动态值的信息存储在请求的上下文中,并且OData应该正确地路由到ODataController。一旦使用了该方法,就可以访问请求上下文以获取有关“动态值”的信息,并使用它来选择正确的数据库

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章