WebApi Controller 分類


前言

      寫這篇WebApi Controller分類一是把Contrller分類的解決方法說一說,再順便把遇到的問題和解決方法聊一聊。 說實話第一次在項目中使用WebApi感覺非常的不順手。

 

遇到的問題和解決方法

     1、多個Get方法。

         Get請求方式是用的最多,而且多處需要重載,很好解決,只需根據參數重載方法即可,如果比較懶的話連路由規則都不需要定義了,直接Get?a=XXX&b=XXX。

     2、多個Post方法。

         如果按照WebApi官方的規則,那么一個WebApi是不能包含多個Post請求的, 至今我也沒找到類似Get重載處理Post重載一樣的方法,后來我發現有人建議直接用Action

  名稱區分(eg. InsertPerson  InsertProduct),然后添加HttpPost屬性,在前台直接調用,那么我考慮如果這樣用的話是不是又違背Restful的設計思想呢?

     3、當遇到Delete請求時調用Delete接口始終沒有調成功。

         一開始我定義Delete接口讓前端調用(ajax發送delete請求),一直報405,后來查了一把,說是需要Web.config配置,又是開啟IIS DELETE和PUT請求,幾種方法全試了最后還是沒搞定,無奈之下我改用了POST請求, PUT請求壓根沒有用。   在群里詢問了一把原來他們是在cs中用HttpClient中調用。

     4、Post多個參數。

        官方建議是使用FromBody標簽,多個參數用Dto封裝。 自定義Post參數綁定類,繼承HttpParameterBinding。然后在Application_Start()中注冊:GlobalConfiguration.Configuration.ParameterBindingRules.Insert(0, SimplePostVariableParameterBinding.HookupParameterBinding);

 

Controller分類

     Controller分類主要是為了解決項目過大以后Controller只有一級編目,項目結構上會顯的非常凌亂,而且會遇到 ApiController文件同名無法解決的問題,提起Controller分類的話大家首先會想到Area, 但實際應用你會發現那真的是為MVC量身打造的,絲毫沒有提WebApi考慮,比如新建Area時文件夾會自動創建Controller、Model、 View, 這些都是不需要的。 那么解決方法就是自己根據項目業務邏輯需要創建Controller編目,最重要的就是自定義Controller Select讓WebApi框架找到你定義的文件結構和ApiController【后面會附帶代碼】。

 

1、這是我Demo里的Controller結構,包含一個一級編目和二級編目(怎么樣,看起來結構一下就清晰多了?)

 

 

2、我把用到的Url匯總成一張表格

Controller名稱 命名空間 url
AdviseController MvcApplication4.Controllers.ContractUs /apix/ContactUs/Advise
ProductController MvcApplication4.Controllers.ContractUs /apix/ContactUs/Product
FinancialController MvcApplication4.Controllers.Products.Enterprise /apixx/Products/Enterprise/Financial
OfficeController MvcApplication4.Controllers.Products.Enterprise /apixx/Products/Enterprise/Office
PuzController MvcApplication4.Controllers.Products.Game /apixx/Products/Game/Puz
RpzController MvcApplication4.Controllers.Products.Game /apixx/Products/Game/Rpg

    url里包含前綴apix和apixx,參考博客園一朋友的寫法,另外我發現,如果一級編目和二級編目里的前綴如果寫成一樣是行不通的,會報No Controller was selected to handle this request,所以你需要根據你的編目結構不同而指定不同的前綴,url后就是Area和Category規則。

 

3、創建完Controller編目結構后就是創建路由規則了(重要)

     在這里指定你的接口前綴 apix,apixx

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

//只有一級編目
config.Routes.MapHttpRoute(
                name:"AreaApi",
                routeTemplate:"apix/{area}/{controller}/{id}",
                defaults:new {id = RouteParameter.Optional});

 //包含二級編目
config.Routes.MapHttpRoute(
                name: "AreaCategoryApi",
                routeTemplate: "apix/{area}/{category}/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional });

 

4、指定完路由規則,如果讓WebApi認識你的接口呢? 自定義Controller Selector,繼承自DefaultHttpControllerSelector, 對了,創建完自定義Controller Selector后別忘了在Application_Start()中注冊,否則也是無效的:

 GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),new ClassifiedHttpControllerSelector(GlobalConfiguration.Configuration));

private const string AREA_ROUTE_VARIABLE_NAME = "area";
        private const string CATEGORY_ROUTE_VARIABLE_NAME = "category";
        private const string THE_FIX_CONTROLLER_FOLDER_NAME = "Controllers";

        private readonly HttpConfiguration m_configuration;
        private readonly Lazy<ConcurrentDictionary<string, Type>> m_apiControllerTypes;

        public ClassifiedHttpControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            m_configuration = configuration;
            m_apiControllerTypes = new Lazy<ConcurrentDictionary<string, Type>>(GetAllControllerTypes);
        }

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            return GetApiController(request);
        }

        private static string GetRouteValueByName(HttpRequestMessage request, string strRouteName)
        {
            IHttpRouteData data = request.GetRouteData();
            if (data.Values.ContainsKey(strRouteName))
            {
                return data.Values[strRouteName] as string;
            }
            return null;
        }

        private static ConcurrentDictionary<string, Type> GetAllControllerTypes()
        {
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Dictionary<string, Type> types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix, StringComparison.OrdinalIgnoreCase) && typeof(IHttpController).IsAssignableFrom(t))).ToDictionary(t => t.FullName, t => t);
            return new ConcurrentDictionary<string, Type>(types);
        }

        private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
        {
            string strAreaName = GetRouteValueByName(request, AREA_ROUTE_VARIABLE_NAME);
            string strCategoryName = GetRouteValueByName(request, CATEGORY_ROUTE_VARIABLE_NAME);
            string strControllerName = GetControllerName(request);
            Type type;
            try
            {
                type = GetControllerType(strAreaName, strCategoryName, strControllerName);
            }
            catch (Exception)
            {
                return null;
            }
            return new HttpControllerDescriptor(m_configuration, strControllerName, type);
        }

        private Type GetControllerType(string areaName, string categoryName, string controllerName)
        {
            IEnumerable<KeyValuePair<string, Type>> query = m_apiControllerTypes.Value.AsEnumerable();
            string strControllerSearchingName;
            if (string.IsNullOrEmpty(areaName))
            {
                strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + controllerName;
            }
            else
            {
                if (string.IsNullOrEmpty(categoryName))
                {
                    strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + areaName + "." + controllerName;
                }
                else
                {
                    strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + areaName + "." + categoryName + "." + controllerName;
                }
            }
            return query.Where(x => x.Key.IndexOf(strControllerSearchingName, StringComparison.OrdinalIgnoreCase) != -1).Select(x => x.Value).Single();
        }

 

總結

     找到了解決方法后,接下來就是重構項目了,對於Delete和Put請求一直還是想解決,還請大家指點指點。  代碼下載 http://pan.baidu.com/s/1bnHQItx

 

參考資料

1、http://www.cnblogs.com/guogangj/archive/2013/03/11/2950084.html

2、http://blogs.infosupport.com/asp-net-mvc-4-rc-getting-webapi-and-areas-to-play-nicely/

 


免責聲明!

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



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