使Web API支持二級實體操作,兼對RESTFul風格API設計的疑惑。


最近一直在糾結應該創建RESTFul風格的API還是以前那種函數調用風格的API。如果創建RESTFul風格的API,又有很多設計問題有待理清,這暫且不論,在用Web API創建RESTFul風格的API的時候,對於二級實體操作又該如何設計API接口呢?比如一個Client實體,它有很多屬於它的Order實體,而每個Order實體又有很多Product實體,API接口如何設計才能更好的體現這種關系和操作呢?如果大家對此有想法,歡迎留言為我解惑。

我目前嘗試設計和實現一種層次性的API接口,我不確定這是否是最佳的做法,調用的時候看起來是這樣的:

/api/Clients/123/Orders/456/Products/789

Route看起來是這樣的:

/api/{controller}/{id}/{subController1}/{subID1}/{subController2}/{subID2}

當然,需要的話,可以繼續往后追加subController3,4,5,6...

而Controller應該看起來是什么樣子的呢?我的做法是,分別為Client、Order和Product建立Controller:

ClientsController

ClientsOrdersController

ClientsOrdersProductsConroller

這樣我可以將以上3個Controller的名字映射到{controller}、{subController1}、{subController2},抽象一點說,就是Controller名字的每一部分對應映射中的一個{controller}/{subcontrollerX}.

剩下的一個問題就是如何讓MVC引擎能將這個路由映射到正確的Controller上。我們都知道(其實我們不都知道,包括在研究這個問題之前的我),MVC引擎根據路由找Controller的操作之一要靠IHttpControllerSelector接口,默認情況下具體干活的就是DefaultHttpControllerSelector類,我要做的就是繼承這個類,然后從路由數據中查看那些subController參數是否被映射上的具體數據,如果沒有,就調用默認行為,如果有,就把所有的controller參數的值拼接成真正的controller名字返回,代碼如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web.Http;
 5 using System.Web.Http.Dispatcher;
 6 using System.Net.Http;
 7 using System.Web.Http.Routing;
 8 
 9 namespace Ricky
10 {
11     public class HttpControllerSelectorEx : DefaultHttpControllerSelector
12     {
13         private HttpConfiguration _HttpCfg;
14 
15         public HttpControllerSelectorEx(HttpConfiguration cfg)
16             : base(cfg)
17         {
18             _HttpCfg = cfg;
19         }
20 
21         public override string GetControllerName(HttpRequestMessage request)
22         {
23             string name = base.GetControllerName(request);
24             IHttpRouteData routeData = request.GetRouteData();
25             IEnumerable<KeyValuePair<string, object>> subControllers = routeData.Values
26                 .Where(d => d.Key.StartsWith("subController", StringComparison.CurrentCultureIgnoreCase));
27             if (subControllers.Count() == 0)
28                 return name;
29 
30             List<string> names = new List<string>(1 + subControllers.Count());
31             names.Add(name);
32             foreach (KeyValuePair<string,object> subController in subControllers)
33             {
34                 names.Add(subController.Value.ToString());
35             }
36 
37             return string.Join("", names);
38         }
39     }
40 }

完成之后需要用我們的controller selector替換系統的默認設置。這在Global.asax.cs的Application_Start方法中做:

1 GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector)
2     , new Ricky.HttpControllerSelectorEx(GlobalConfiguration.Configuration));

最后,我們修改一下路由設置:

 1 public static void Register(HttpConfiguration config)
 2 {
 3     config.Routes.MapHttpRoute(
 4         name: "DefaultApiWithSubControllers",
 5         routeTemplate: "api/{controller}/{id}/{subController}/{subID}/{subController2}/{subID2}",
 6         defaults: new
 7         {
 8             id = RouteParameter.Optional,
 9             subController = RouteParameter.Optional,
10             subID = RouteParameter.Optional,
11             subController2 = RouteParameter.Optional,
12             subID2 = RouteParameter.Optional
13         }
14     );
15 
16     //config.Routes.MapHttpRoute(
17     //    name: "DefaultApi",
18     //    routeTemplate: "api/{controller}/{id}",
19     //    defaults: new { id = RouteParameter.Optional }
20     //);
21 }

由於我們的路由設置其實是擴展默認設置的,因此可以注釋掉原來的默認設置。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM