2013-11-14 106 views
52

我在玩基本控制器的想法,即使用通用資源庫爲我的API控制器提供基本的CRUD方法,這樣我就不必重複相同的基本代碼在每個新的控制器。但是我遇到了路由屬性在基本控制器中被識別的問題。爲了確切地顯示我遇到的問題,我創建了一個非常簡單的WebAPI控制器。.NET WebAPI屬性路由和繼承

當我有主控制器中的Get方法,它直接從ApiController繼承我沒有任何問題,並按預期工作。

[RoutePrefix("admin/test")] 
public class TestController : ApiController 
{ 
    [Route("{id:int:min(1)}")] 
    public string Get(int id) 
    { 
     return "Success"; 
    } 
} 

當我將Get方法移動到基本控制器時,它將返回404頁面的內容。

[RoutePrefix("admin/test")] 
public class TestController : TestBaseController 
{ 

} 

public class TestBaseController : ApiController 
{ 
    [Route("{id:int:min(1)}")] 
    public string Get(int id) 
    { 
     return "Success"; 
    } 
} 

一些更有趣的注意事項:

  • 我可以在GET /測試/ 1訪問的動作。所以它仍然基於默認路由來查找它。

  • 當我嘗試訪問POST /管理/測試返回以下JSON

    { 「消息」: 「沒有HTTP資源發現匹配的請求URI 'http://test.com/admin/test'。」 「MessageDetail 「:」找不到與名爲'admin'的控制器匹配的類型。「 }

有誰知道的一種方式來獲得的路由與從基本控制器屬性工作?

+2

我試圖完成同樣的事情 - 使用基本API控制器來處理CRUD - 我很驚訝,這不是更常見。這是我第一次看到其他人試圖提到這一點,並且我現在堅持路由繼承問題。你能分享你最終做了什麼嗎? – Brett

+2

我有基礎控制器有保護的方法來實現CRUD操作,然後當我創建一個新的控制器時,我必須創建公共方法並讓它調用受保護的方法。通過這種方式,我可以將路由放在控制器中的方法中,而不是放在基本控制器中,也不必在任何地方複製代碼。我不喜歡它,但另一種方法是不使用Route屬性,並且由於各種原因,這種情況不適用於我的情況。 –

+0

感謝您的回覆。我使用了類似的東西 - 只需調用base [Get | Post | Put | Delete]()並傳遞參數。這並不理想,但是我認爲好處在於,對於不熟悉代碼的用戶來說,放置自定義CRUD操作更爲明顯。你有沒有發現其他在線討論使用基本控制器來處理CRUD操作的資源?我的搜索讓我沒有結果。 – Brett

回答

60

屬性路由不能被繼承。這是一個故意的設計決定。我們感覺不對,並沒有看到有效的場景,它們可以繼承它們。

你可以給出一個更現實的場景,你想在哪裏使用它?

[更新(2014年3月24日)
在即將到來的5.2版本MVC的Web API的,有將被稱爲System.Web.Http.Routing.IDirectRouteProvider的擴展點,通過它可以使你正在尋找的繼承方案這裏。你可以使用最新的夜晚建立自己試用(文檔瞭解如何使用夜間構建是here

[更新(2014年7月31日)這是如何在網絡API 2.2
例版本:

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider()); 

//--------- 

public class CustomDirectRouteProvider : DefaultDirectRouteProvider 
{ 
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor) 
    { 
     // inherit route attributes decorated on base class controller's actions 
     return actionDescriptor.GetCustomAttributes<IDirectRouteFactory> 
     (inherit: true); 
    } 
} 
+3

以下是我將要玩弄的內容。我有一個通用的存儲庫,並將創建一個RepositoryBaseApiController,它加載了一個IRepository 用於基本的CRUD操作。這樣,如果我需要爲一個實體提供一個真正簡單的WebAPI,我可以創建一個SomeEntityController並繼承RepositoryBaseApiController ,它會照顧到我所有的基礎知識。我不必每次都創建操作,驗證檢查和路由。我會在主控制器上使用RoutePrefix(),但基本控制器會有相應的路由 –

+0

感謝您的場景。我們考慮過這種情況,但在某些情況下,用戶可能會在基礎控制器中擁有非常特定的路由模板,在這種情況下繼承此子類是不正確的... –

+0

我有一堆處理文件轉換器的控制器。每個人都有與上傳文件有關的共同終結點,而我沒有繼承的唯一選擇是在操作中立即重複代碼或委託給助手,非常難看。 AttributeRouting支持這個原因爲什麼不會本地實現https://github.com/mccalltd/AttributeRouting/wiki/Inherit-Routes-from-Actions-in-Base-Controllers – parliament

3

明白了。

[Route("api/baseuploader/{action}")] 
public abstract class BaseUploaderController : ApiController 
{ 
    [HttpGet] 
    public string UploadFile() 
    { 
     return "UploadFile"; 
    } 
} 


[Route("api/values/{action}")] 
public class ValuesController : BaseUploaderController 
{ 
    [HttpGet] 
    public string Get(int id) 
    { 
     return "value"; 
    } 
} 

這裏需要注意的一點是,路由動作參數必須與動作名稱相同。我無法找到解決辦法。 (您無法使用RouteAttribute重命名路由)

+0

這不工作? –

+0

更新爲經過測試的工作實施。請反向downvote,如果它適合你:) – parliament

+0

將再次測試。那麼api/values/uploadfile會給「uploadfile」?這樣做只是因爲路由屬性中的{action}而起作用,並且您仍然可以使用路由屬性與它一起:) –

21

使用Web API 2。2,您可以:

public class BaseController : ApiController 
{ 
    [Route("{id:int}")] 
    public string Get(int id) 
    { 
     return "Success:" + id; 
    } 
} 
[RoutePrefix("api/values")] 
public class ValuesController : BaseController 
{ 
} 

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider()); 
public class CustomDirectRouteProvider : DefaultDirectRouteProvider 
{ 
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor) 
    { 
     return actionDescriptor.GetCustomAttributes<IDirectRouteFactory> 
     (inherit: true); 
    } 
} 

如下概括:http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22