2015-04-08 132 views
3

使用Web Api我有一個OData EndPoint可以從數據庫返回產品。在OData中傳遞參數WebApi Url

我有多個具有相似模式的數據庫,並且想要在URL中傳遞一個參數來確定Api應該使用哪個數據庫。

當前的OData端點:
http://localhost:62999/Products

我想要什麼:
http://localhost:62999/ /產品

在新網址,我通過999(數據庫ID)。

數據庫ID旨在指定從哪個數據庫加載產品。例如,localhost:62999/999/Products('ABC123')會從數據庫999加載產品'ABC123',但下一個請求localhost:62999/111/Products('XYZ789')會從數據庫111加載產品'XYZ789'。

下面的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<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參數的地方嗎? 我也想過使用消息處理程序,但我不確定如何實現這一點。

UPDATE
這個問題是試圖做同樣的事情,我說:How to declare a parameter as prefix on 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 } 
    ); 
} 
+0

只是爲了澄清......你是否想用ID來指定哪個單從數據庫中選擇產品,還是從哪個數據庫中選擇所有產品? –

+1

它旨在指定從哪個數據庫加載產品。例如'localhost:62999/999/Products('ABC123')'會從數據庫999加載產品'ABC123',但下一個請求'localhost:62999/111/Products('XYZ789')'會加載產品' XYZ789'來自數據庫111. – philreed

+0

嗨philreed你解決了你的問題嗎?我建議你的問題的解決方案 –

回答

1

我遇到溶液通過動態參數上的OData,不知道是否是正確的。

我在某個上下文中使用了這個解決方案,其中動態參數只是爲了驗證客戶端,但我認爲你可以用類似的方式解決你的問題。

問題:您wan't在URL請求,例如通過一個動態值:http://localhost:62999/ {} dynamicValue /產品( 'ABC123'),但ODataRouting永遠路線正確,因爲額外的/ {dynamicValue的}和ODataControler「不會命中」。 使用ApiController你可以做一個自定義的路由,但在OData你不能(至少我沒有找到一個簡單的方法來做到這一點,可能你必須自己創建或擴展OData路由約定)。

所以作爲替代解決方案: 如果每個請求將具有例如dynamicValue:「http://localhost:62999/ {dynamicValue} /產品」執行以下步驟:

  1. 路由請求提取dynamicValue之前(在我的情況是,我使用IAuthenticationFilter在路由之前攔截郵件,因爲參數與授權有關,但對於您的情況,使用另一件事情更有意義)
  2. 將dynamicValue(位於請求上下文)
  3. 路由不帶{dynamicValue}的ODataController。 /產品( 'ABC123'),而不是/ {dynamicValue} /產品( '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。一旦你在那裏使用你的方法,你可以訪問請求上下文以獲得關於「動態值」的信息並用它來選擇正確的數據庫

+0

我現在有這個工作,但我不得不對你的答案做一些改變。我仍然希望在您給解決方案的正確路徑上給予我信任,但您發佈的答案並非我確切使用的答案。 – philreed

+1

主要變化是在配置OData路由時定義路由模板:'routePrefix:「erp/{company}」'。這允許我調用'var company = HttpContext.Current.Request.RequestContext.RouteData.Values [「company」];'(帶有所需的空檢查),這意味着我沒有必要解析字符串尋找您在過濾器中使用'/'索引。 – philreed