AreaHttpControllerSelector 對 Web Api 實現 Area 路由控制

微酷發表於2018-10-27

結合此文章:http://www.cnblogs.com/wuhuacong/p/5828038.html

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Routing;

namespace PEMSoft.Web.AppServer
{
    /// <summary>
    /// Area路由控制,如URL中含{action}則通過Area查詢控制器,不存在{area}或找不到控制器則按預設規則再找一次
    /// 作者:ifu25
    /// 日期:2017/07/08
    /// 參考:http://www.jianshu.com/p/8242215cd8c0 、 http://www.cnblogs.com/Silicon-Fado/p/3571938.html
    /// </summary>
    public class AreaHttpControllerSelector : DefaultHttpControllerSelector
    {
        #region 變數

        /// <summary>
        /// Area名稱
        /// </summary>
        private const string _areaRouteVariableName = "area";

        private readonly HttpConfiguration _configuration;

        private Dictionary<string, Type> _apiControllerTypes;

        /// <summary>
        /// 所有API控制器集合
        /// </summary>
        private Dictionary<string, Type> ApiControllerTypes
        {
            get { return _apiControllerTypes ?? (_apiControllerTypes = GetControllerTypes()); }
        }

        #endregion

        /// <summary>
        /// 構造
        /// </summary>
        public AreaHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
        {
            _configuration = configuration;
        }

        /// <summary>
        /// 覆寫父類的選擇控制器方法,通過此方法實現根據Area查詢控制器
        /// </summary>
        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            //選通過Area查詢控制器,找不到則忽略Area再找一次
            return GetApiController(request) ?? base.SelectController(request);
        }

        #region 內部方法

        /// <summary>
        /// 獲取所有控制器
        /// </summary>
        private static Dictionary<string, Type> GetControllerTypes()
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            var types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix) && typeof(IHttpController).IsAssignableFrom(t)))
                .ToDictionary(t => t.FullName, t => t);

            return types;
        }

        /// <summary>
        /// 為給定的HttpRequestMessage選擇帶Area的控制器,沒有Area則返回null
        /// </summary>
        private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
        {
            var controllerName = base.GetControllerName(request);

            var areaName = GetAreaName(request);
            if (string.IsNullOrEmpty(areaName))
            {
                return null;
            }

            var type = GetControllerTypeByArea(areaName, controllerName);
            if (type == null)
            {
                return null;
            }

            return new HttpControllerDescriptor(_configuration, controllerName, type);
        }

        /// <summary>
        /// 從HttpRequestMessage獲取Area,沒有指定Area則返回null
        /// </summary>
        private static string GetAreaName(HttpRequestMessage request)
        {
            var data = request.GetRouteData();

            if (!data.Values.ContainsKey(_areaRouteVariableName))
            {
                return null;
            }

            return data.Values[_areaRouteVariableName].ToString().ToLower();
        }

        /// <summary>
        /// 獲取指定Area下的控制器
        /// </summary>
        /// <param name="areaName">Area名稱</param>
        /// <param name="controllerName">控制器名稱</param>
        private Type GetControllerTypeByArea(string areaName, string controllerName)
        {
            var areaNameToFind = string.Format(".{0}.", areaName.ToLower());
            var controllerNameToFind = string.Format(".{0}{1}", controllerName, ControllerSuffix);

            return ApiControllerTypes.Where(t => t.Key.ToLower().Contains(areaNameToFind) && t.Key.EndsWith(controllerNameToFind, StringComparison.OrdinalIgnoreCase))
                    .Select(t => t.Value).FirstOrDefault();
        }

        #endregion
    }
}

相關文章