1. 程式人生 > 實用技巧 >Asp.net core 3.1實現路由Url根據名稱空間自動生成

Asp.net core 3.1實現路由Url根據名稱空間自動生成

Asp.net core預設的介面寫法,每個介面控制器都得繼承自ControllerBase類,還需具有ApiController特性及Route特性,Route特性上還得手動寫上路由規則。如下圖所示

我覺得挺麻煩的,可能我比較懶,其實我的寫的大多數的介面路徑都是按一定規則的,一般是以名稱空間Controllers.後面的名稱空間做為路徑字首,然後以[controller]/[action]路由規則進行匹配。

例如名稱空間LessSharp.WebApi.Controllers.Sys下面的UserController控制器下面的Save介面,那麼根據名稱空間的後面Sys,對應的介面路徑值就為/Sys/User/Save

那麼我希望預設情況下,可以以我上面的規則自動生成路徑,如果有特殊情況的話,那我加個Route特性,手動寫上我的路由規則,框架就優先按我Route特性定義的路由路徑值。

我實現的方法主要使用了Asp.net Core提供的控制器模型約定ControllerModelConvention功能,再借助ApiConventionController控制器基類,路由生成只會在系統啟動進行生成,所以對系統性能無任何影響。

以下是ApiConventionController的程式碼,這個類其實作用不大,主要起個標記作用,只要繼承這個控制器基類的控制器就說明要使用以上的Url生成規則。

namespace
LessSharp.WebApi.Controllers { /// <summary> /// 繼承此控制器後,會自動根據名稱空間結構修改Route增加字首 /// 例如LessSharp.WebApi.Controllers.Sys.A.B名稱空間下的UserController控制器,就會生成Sys/A/B/User這樣的路由 /// 如果派生控制器有自定義Route特性的話,就不會自動增加字首 /// </summary> [Route("[controller]/[action]")] [ApiController] public abstract
class ApiConventionController : ControllerBase { }

以下是這個Url生成功能最核心的類ApiRoutePrefixControllerModelConvention,它實現IControllerModelConvention介面

namespace LessSharp.WebApi.Conventions
{
    public class ApiRoutePrefixControllerModelConvention : IControllerModelConvention
    {
        /// <summary>
        /// 路徑字首
        /// </summary>
        private readonly string _prefix;
        public ApiRoutePrefixControllerModelConvention(string prefix)
        {
            _prefix = prefix;
        }
        public void Apply(ControllerModel controller)
        {
            //判斷是否是ApiConventionController的派生控制器
            if (controller.ControllerType.BaseType != typeof(ApiConventionController))
            {
                return;
            }
            //判斷是否有自定義Route特性
            if (controller.ControllerType.GetCustomAttributes(typeof(RouteAttribute), false).Length > 0)
            {
                return;
            }
            string controllerNamespace = controller.ControllerType.Namespace;
            string temp = "Controllers.";
            int index = controllerNamespace.IndexOf(temp);
            string prefix = _prefix.Trim('/');
            if (index > -1)
            {
                prefix += "/" + controllerNamespace.Substring(index + temp.Length);
            }
            if (string.IsNullOrEmpty(prefix))
            {
                return;
            }
            if (!string.IsNullOrEmpty(prefix))
            {
                prefix = prefix.Replace(".", "/");
            }
            
            foreach (var selector in controller.Selectors.Where(s => s.AttributeRouteModel != null))
            {
                selector.AttributeRouteModel.Template = prefix + "/" + selector.AttributeRouteModel.Template;
            }
        }
    }
}

最後在Startup的ConfigureServices方法裡新增這個約定。我這裡還添加了根據配置檔案的RoutePrefix值統一新增Url字首的功能

services.AddControllers(o =>
{//新增約定器,對ApiConventionController的派生類新增路由字首
    o.Conventions.Add(new ApiRoutePrefixControllerModelConvention(Configuration.GetValue<string>("RoutePrefix")));
})

最後你只要這麼使用就行了,又省了不少程式碼,哈哈哈