使用Web Api我有一个OData EndPoint,它可以从数据库返回产品.
我有多个具有相似模式的数据库,并希望在URL中传递一个参数来识别Api应该使用哪个数据库.
当前Odata终点:
http://localhost:62999/Products
我想要的是:
http://localhost:62999/999 /产品
在新的Url中,我传入了999(数据库ID).
数据库ID用于指定从中加载产品的数据库.例如localhost:62999/999 / Products(‘ABC123′)将从数据库999加载产品’ABC123’,但是下一个请求localhost:62999/111 / Products(‘XYZ789′)将从数据库加载产品’XYZ789’ 111.
下面的Url有效,但我不喜欢它.
本地主机:62999 /产品( ‘XYZ789’)数据库= 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参数的地方吗?
我也考虑过使用Message Handler,但我不确定如何实现它.
UPDATE
这个问题试图和我做同样的事情:How to declare a parameter as prefix on OData
但目前尚不清楚如何从网址读取参数.
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 }
);
}
解决方法:
我遇到过在OData上传递动态参数的解决方案,不确定它是否正确.
我在某个上下文中使用了这个解决方案,其中动态参数只是为了验证客户端,但我认为你可以用类似的方式解决你的问题.
问题:您不想在URL请求示例中传递动态值:http://localhost:62999/ {dynamicValue} / Products(‘ABC123’),但ODataRouting将永远不会正确路由,因为额外的/ {dynamicValue}和ODataControler“将不打“.
使用ApiController你可以做一个自定义路由,但在OData你不能(至少我没有找到一个简单的方法来做,可能你必须自己做或扩展OData路由约定).
作为替代解决方案:
如果每个请求都有一个dynamicValue,例如:“http://localhost:62999/ {dynamicValue} / Products”,请执行以下步骤:
>在路由请求之前提取dynamicValue(在我的情况下,我使用IAuthenticationFilter在路由之前拦截消息,因为参数与授权有关,但是对于你的情况,使用另一个东西更有意义)
>存储dynamicValue(在请求上下文的某个位置)
>在没有{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.在您的方法中,您可以访问请求上下文以获取有关“动态值”的信息并使用它来选择正确的数据库