在OData WebApi Url中传递参数

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

我有多个具有相似模式的数据库,并希望在URL中传递一个参数来识别Api应该使用哪个数据库。

当前的Odata终点:
HTTP://本地主机:62999 /产品

我想要的是:
http:// localhost:62999/999 /产品

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

数据库ID用于指定从中加载产品的数据库。 例如localhost:62999/999/Products('ABC123')将从数据库999加载产品’ABC123’,但是下一个请求localhost:62999/111/Products('XYZ789')将从数据库加载产品’XYZ789′ 111。

下面的Url有效,但我不喜欢它。
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 Get(ODataQueryOptions queryOptions) { return ErpService.Products(queryOptions); } [EnableQuery] public SingleResult Get([FromODataUri] string key, ODataQueryOptions queryOptions) { var result = ErpService.Products(queryOptions).Where(p => p.StockCode == key); return SingleResult.Create(result); } } 

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

kernel.Bind().ToProvider(new ErpServiceProvider()); 并且ErpServiceProvider检查URL以识别此请求所需的databaseId:

 public class ErpServiceProvider : Provider { 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("Products"); builder.EntitySet("WorkOrders"); config.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: null, model: builder.GetEdmModel()); 

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

UPDATE
这个问题试图和我做同样的事情: 如何在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("Products"); builder.EntitySet("WorkOrders"); config.MapODataServiceRoute( routeName: "ODataRoute", routePrefix: "{company}/", model: builder.GetEdmModel()); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } 

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

我在某个上下文中使用了这个解决方案,其中动态参数只是为了validation客户端,但我认为你可以用类似的方式解决你的问题。

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

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

  1. 在路由请求之前提取dynamicValue(在我的情况下,我使用IAuthenticationFilter在路由之前拦截消息,因为参数与授权相关,但是对于你的情况,使用另一个东西更有意义)
  2. 存储dynamicValue(在请求上下文的某个位置)
  3. 在没有{dynamicValue}的情况下路由ODataController。 /产品(’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。 在您的方法中,您可以访问请求上下文以获取有关“动态值”的信息并使用它来选择正确的数据库