传递参数在OData的网址的WebAPI [英] Pass Parameters in OData WebApi Url

查看:125
本文介绍了传递参数在OData的网址的WebAPI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用网页API我有一个OData的端点,可以从数据库返回的产品。

我有类似的架构多个数据库,并希望传递参数的URL识别API应该使用的数据库。

当前的OData端点:结果
的http://本地主机:62999 /产品

我要什么:结果
的http://本地主机:62999 / 999 /产品

在新的网址,我通过在999(数据库ID)。

数据库ID旨在指定数据库以从加载的产物。例如本地主机:999分之62999/产品(ABC123')将加载从数据库999产品ABC123,但下一个请求,本地主机:一百一十一分之六万二千九百九十九/产品('XYZ789')将从数据库111装载产品的XYZ789'。

以下作品的网址,但我不喜欢它。结果
本地主机:62999 /产品('XYZ789')数据库= 111

下面是code为控制器:

 公共ProductsController类:// ErpApiController延伸ODataController,处理数据库资源配置
{
    公众的ProductsController(IErpService erpService):基地(erpService){}    [EnableQuery(每页= 50)]
    公众的IQueryable< ProductDto>获取(ODataQueryOptions< ProductDto> queryOptions)
    {
        返回ErpService.Products(queryOptions);
    }    [EnableQuery]
    公共SingleResult< ProductDto>获取([FromODataUri]串键,ODataQueryOptions< ProductDto> queryOptions)
    {
        VAR的结果= ErpService.Products(queryOptions)。凡(P => p.Stock code ==键);
        返回SingleResult.Create(结果);
    }
}

我使用Ninject通过结合到一个服务提供者,以解决其IErpService的实施注入到控制器:

kernel.Bind< IErpService方式>()ToProvider(新ErpServiceProvider());
 和 ErpServiceProvider 检查URL,以确定该请求所需的databaseId:

 公共类ErpServiceProvider:提供< IErpService>
{
    保护覆盖IErpService的CreateInstance(IContext上下文)
    {
        VAR databaseId = HttpContext.Current.Request [数据库];        返回新SageErpService(新SageContext(GetDbConnection(databaseId)));
    }
}

我被困在是如何来定义的OData路线配置URL参数位。

普通的WebAPI的路线可以有定义的参数如下:

  config.Routes.MapHttpRoute(
            名称:DefaultApi
            routeTemplate:API / {}控制器/(编号),
            默认:新{ID = RouteParameter.Optional}
      );

但我怎么在OData的航线配置定义的参数?

  ODataModelBuilder建设者=新ODataConventionModelBuilder();
        builder.EntitySet< ProductDto>(产品);
        builder.EntitySet&所述; WorkOrderDto&GT(工作订单);
        config.MapODataServiceRoute(
            routeName:ODataRoute
            路线preFIX:空,
            型号:builder.GetEdmModel());

这是即使我应该定义URL的参数?
我也想过使用一个消息处理程序,但我不能肯定这可怎么要么执行。

更新结果
这个问题是试图做同样的事情,我:<一href=\"http://stackoverflow.com/questions/27827188/how-to-decalre-a-parameter-as-$p$pfix-on-odata\">How到decalre参数作为OData的 preFIX
结果
但它是不明确的参数是如何从网址读取。结果
VAR databaseId = HttpContext.Current.Request [数据库]; 返回当前空结果即使更新路由配置以下后:

 公共静态无效的注册(HttpConfiguration配置)
{
    //网页API路线
    config.MapHttpAttributeRoutes();    config.Routes.MapHttpRoute(
        名称:ErpApi
        routeTemplate:{数据库} / {}控制器
    );    //网页API的配置和服务
    ODataModelBuilder建设者=新ODataConventionModelBuilder();
    builder.EntitySet&LT; ProductDto&GT;(产品);
    builder.EntitySet&所述; WorkOrderDto&GT(工作订单);
    config.MapODataServiceRoute(
        routeName:ODataRoute
        路线preFIX:{}公司/,
        型号:builder.GetEdmModel());    config.Routes.MapHttpRoute(
        名称:DefaultApi
        routeTemplate:API / {}控制器/(编号),
        默认:新{ID = RouteParameter.Optional}
    );
}


解决方案

我遇到一个解决方案,通过对OData的动态参数,不知道是否是正确的。

我已经用在一定的语境,这里的动态参数只是验证客户端这个解决方案,但我认为你可以以类似的方式解决问题。

问题:您wan't在URL请求的例子来传递一个动态值:的http:/ /本地主机:62999 / {} dynamicValue /产品(ABC123),但ODataRouting永远正确的路线,因为这些额外的/ {} dynamicValue和ODataControler不会打。
使用ApiController你可以做一个自定义的路由,但在OData的你不能(至少我没有找到一个简单的方法来做到这一点,也许你不得不做出自己的或扩展的OData路由约定)。

因此​​,作为替代的解决方法:
如果每个请求都会有例如dynamicValue:的http://本地主机:62999 / {} dynamicValue /产品吗以下步骤:


  1. 将请求路由提取dynamicValue之前(就我而言,我已经使用了IAuthenticationFilter拦截消息据路由之前,因为参数与授权有关,但也许对于你的情况这让使用另一种更有意义事情)

  2. 存放dynamicValue(某处请求上下文)

  3. 路线而不{} dynamicValue的ODataController。
    /产品(ABC123),而不是/ {} dynamicValue /产品(ABC123')

下面是code:

  //注册ServiceRoute
公共静态无效的注册(HttpConfiguration配置)
{  //注册之前,它植根于OData的将拦截请求过滤器
  config.Filters.Add(CustomAuthenticationFilter&GT;()); //如果你的动态参数与身份验证有关使用IAuthenticationFilter否则你可以注册例如的MessageHandler。  //创建内置约定的默认集合。
  VAR约定= ODataRoutingConventions.CreateDefault();  config.MapODataServiceRoute(
          routeName:NameOfYou​​rRoute
          路线preFIX:空,//在这里你可以,如果你想定义preFIX
          型号:GetEdmModel()//获取模型
          pathHandler:新CustomPathHandler(),//使用CustomPath来处理动态参数
          routingConventions:约定); //使用默认的路由公约
}//只是一个过滤器拦截消息它击中控制器之前,并提取与放大器;存储DynamicValue
公共类CustomAuthenticationFilter:IAuthenticationFilter,IFilter的
{
   //提取动态值
   VAR dynamicValueStr =((串)context.ActionContext.RequestContext.RouteData.Values​​ [odatapath])
        .Substring(0,((串)context.ActionContext.RequestContext.RouteData.Values​​ [odatapath])
        。指数('/')); //你可以使用一个更安全的方式解析   INT dynamicValue;
   如果(int.TryParse(dynamicValueStr,出dynamicValue))
   {
      // TODO(这个我把它留给你:))
      //存储在某个地方,可能是在要求背景
      //例如如权利要求
   }
}//定义你的自定义路径处理程序
公共类CustomPathHandler:DefaultODataPathHandler
{
    公众覆盖ODataPath解析(IEdmModel型号,串serviceRoot,串odataPath)
    {
        // code提出删除dynamicValue
        //这是假设的dynamicValue是在第一/
        INT dynamicValueIndex = odataPath.IndexOf('/');
        odataPath = odataPath.Substring(dynamicValueIndex + 1);        //现在的OData将路由normaly,因为路线只会有请求/产品(ABC123)
        返回base.Parse(型号,serviceRoot,odataPath);
    }
}

现在你应该存储在请求的上下文的动态值的信息,并应OData的正确路线的ODataController。一旦你有你的方式,你可以访问请求上下文来获取有关动态价值的信息,并用它来选择正确的数据库

Using Web Api I have an OData EndPoint which can return Products from a database.

I have multiple databases with similar schemas, and want to pass a parameter in the URL to identify which database the Api should use.

Current Odata Endpoint:
http://localhost:62999/Products

What I want:
http://localhost:62999/999/Products

In the new Url, I pass in 999 (the database ID).

The database ID is intended to specify which database to load the product from. For example localhost:62999/999/Products('ABC123') would load product 'ABC123' from database 999, but the next request, localhost:62999/111/Products('XYZ789') would load the product 'XYZ789' from database 111.

The Url below works, but I don't like it.
localhost:62999/Products('XYZ789')?database=111

Here is the code for the controller:

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

I use Ninject to resolve which implementation of IErpService to inject into the controller by binding to a service provider:

kernel.Bind<IErpService>().ToProvider(new ErpServiceProvider()); And the ErpServiceProvider inspects the url to identify the databaseId required by this request:

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

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

The bit I am stuck on is how to define the Url parameter in the OData route config.

Normal WebApi routes can have parameters defined as follows:

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

But how do I define the parameters in the OData route config?

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

Is this even where I should be defining the Url parameters? I have also thought about using a Message Handler but I am not certain how this can be implemented either.

UPDATE
This question is trying to do the same thing as me: How to decalre a parameter as prefix on OData
But it is not clear how the parameter is to be read from the url.
var databaseId = HttpContext.Current.Request["database"]; returns null currently.
Even after updating the route config to the following:

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

解决方案

I've encountered a solution to pass dynamic parameter on OData, not sure if is the right one.

I've used this solution on a certain context, where the dynamic parameter was just to authenticate the client, but I think you can solve your problem in a similar way.

Problem: You wan't to pass a dynamic value at the URL request example: http://localhost:62999/{dynamicValue}/Products('ABC123'), but the ODataRouting will never route correctly, because of that extra /{dynamicValue} and the ODataControler "will not hit". Using ApiController you could made a custom routing, but at OData you can't (at least I didn't found an easy way to do it, probably you had to made your own or extend the OData routing convention).

So as alternative solution: If every request will have a dynamicValue for example: "http://localhost:62999/{dynamicValue}/Products" do the following steps:

  1. Before routing the request Extract the dynamicValue (In my case, I've used an IAuthenticationFilter to intercept the message before it was routed, since the parameter was related with authorization, but maybe for your case it makes more sense to use another thing)
  2. Store the dynamicValue (somewhere on the request context)
  3. Route the ODataController without the {dynamicValue}. /Products('ABC123') instead of /{dynamicValue}/Products('ABC123')

Here is the code:

// 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);
    }
}

Now you should have the information of the dynamic value stored at the context of the request and OData should route correctly to the ODataController. Once your there at your method, you can access the request context to get information about the "dynamic value" and use it to choose the correct database

这篇关于传递参数在OData的网址的WebAPI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆