• WebApi 重写 DefaultHttpControllerSelector 实现路由重定向


    背景:

    本项目为后台项目

    近期项目组内推行前后端分离架构。前端使用vue,后端使用webapi进行重构。

    因原项目为mvc,所以重构对于后台接口变化不大。

    新建webapi项目,log4net引入,swagger引入,全局异常处理,实现登陆登出功能,实现登陆过滤器,实现token安全机制,规范下接口返回模型等等。

    前端使用vue后url路由由前端接管,后端只用实现功能需要的数据接口和一个返回前端初始化资源的初始页面暂定Web/Index。

    通常实现是,登陆后重定向到Web/Index页面,Web/Index页面输出前端初始化资源,后续路由跳转由前端接管。

    但因为是旧项目重构,只能一部分一部分切换为vue+webapi的新架构,需要和mvc的旧项目共用一段时间。通过菜单url的不同来确定跳转到新或者旧项目

    这样就需要额外实现:新旧项目登陆/登出联动,并且保证新旧系统框架样式(菜单,头部,底部)保持一致。

    还引发一个问题:从旧项目可以跳转到任意一个新项目功能页面,并且需要输出前端初始化资源,后续路由跳转就由前端接管了。

    这样就需要很多路由规则都匹配到Web/Index,然后输出前端初始化资源

    原实现方式:

     1 using System.Net;
     2 using System.Net.Http;
     3 using System.Net.Http.Headers;
     4 using System.Web.Http;
     5 
     6 namespace AppApi.Controllers
     7 {
     8     /// <summary>
     9     /// 
    10     /// </summary>
    11     public class WebController : ApiController
    12     {
    13         #region Public APIs
    14 
    15         /// <summary>
    16         /// Index
    17         /// </summary>
    18         /// <returns></returns>
    19         [Route("fe/dashboard")]
    20         [Route("fe/Product/Index")]
    21         [Route("fe/User/Index")]
    22         [Route("fe/Order/Index")]
    23         [Route("fe/System/Menu/Index")]
    24         [Route("fe/System/Config/Index")]
    25         [HttpGet]
    26         public HttpResponseMessage Index()
    27         {
    28             //前端资源
    29             string resourceHtml = "";
    30             var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(resourceHtml) };
    31             response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
    32             return response;
    33         }
    34         #endregion
    35     }
    36 }

    这种方式需要定义大量的路由规则。

    新的实现方式:

    使用重写DefaultHttpControllerSelector(Controller选择器),ApiControllerActionSelector(Action选择器)实现

    因前端路由都包含 /fe/ ,所以我们就拦截所有包含 /fe/ 的url

    1,重写 DefaultHttpControllerSelector 的 GetControllerName 方法

    新建文件:CustomControllerSelector.cs

     1 using System.Net.Http;
     2 using System.Web.Http;
     3 using System.Web.Http.Dispatcher;
     4 
     5 namespace AppApi.App_Start
     6 {
     7     /// <summary>
     8     /// 自定义Controller选择
     9     /// </summary>
    10     public class CustomControllerSelector : DefaultHttpControllerSelector
    11     {
    12         /// <summary>
    13         /// 
    14         /// </summary>
    15         /// <param name="configuration"></param>
    16         public CustomControllerSelector(HttpConfiguration configuration) : base(configuration)
    17         {
    18         }
    19 
    20         /// <summary>
    21         /// 摘要:获取指定 System.Net.Http.HttpRequestMessage 的控制器的名称。
    22         /// </summary>
    23         /// <param name="request">参数:request:HTTP 请求消息。</param>
    24         /// <returns>返回结果:指定 System.Net.Http.HttpRequestMessage 的控制器的名称。</returns>
    25         public override string GetControllerName(HttpRequestMessage request)
    26         {
    27             //Index规则(/fe/)满足的话,返回Web/Index
    28             if (request.RequestUri.AbsoluteUri.Contains("/fe/"))
    29             {
    30                 return "Web";
    31             }
    32             else
    33             {
    34                 return base.GetControllerName(request);
    35             }
    36         }
    37     }
    38 }

    ,2,重写 ApiControllerActionSelector 的 SelectAction 方法

    新建文件:CustomActionSelector.cs

     1 using System.Linq;
     2 using System.Web.Http.Controllers;
     3 
     4 namespace AppApi.App_Start
     5 {
     6     /// <summary>
     7     /// 自定义Action选择
     8     /// </summary>
     9     public class CustomActionSelector : ApiControllerActionSelector
    10     {
    11         /// <summary>
    12         /// 
    13         /// </summary>
    14         /// <param name="controllerContext"></param>
    15         /// <returns></returns>
    16         public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
    17         {
    18             //Index规则(/fe/)满足的话,返回Web/Index
    19             if (controllerContext.Request.RequestUri.AbsoluteUri.Contains("/fe/"))
    20             {
    21                 return base.GetActionMapping(controllerContext.ControllerDescriptor).FirstOrDefault(f => f.Key == "Index").FirstOrDefault(d => d.ActionName == "Index");
    22             }
    23             else
    24             {
    25                 return base.SelectAction(controllerContext);
    26             }
    27         }
    28     }
    29 }

    3,然后添加路由规则

    1             // WebIndex路由,返回前端资源
    2             config.Routes.MapHttpRoute(
    3                 name: "WebIndex",
    4                 routeTemplate: "fe/{*.}",//catch-all
    5                 defaults: new { controller = "Web", action = "Index" }
    6                 );

    注意:routeTemplate: "fe/{*.}",//catch-all

    使用 fe/{*.} 不管路由由几级都可以匹配

    4,使自定义ControllerSelector,ActionSelector生效

    1             // 使用自定义ControllerSelector,ActionSelector
    2             config.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector(config));
    3             config.Services.Replace(typeof(IHttpActionSelector), new CustomActionSelector());

    修改文件:WebApiConfig.cs

     1 using AppApi.App_Start;
     2 using Newtonsoft.Json.Serialization;
     3 using System.Web.Http;
     4 using System.Web.Http.Controllers;
     5 using System.Web.Http.Dispatcher;
     6 
     7 namespace AppApi
     8 {
     9     /// <summary>
    10     /// webapi配置
    11     /// </summary>
    12     public static class WebApiConfig
    13     {
    14         /// <summary>
    15         /// webapi注册
    16         /// </summary>
    17         /// <param name="config"></param>
    18         public static void Register(HttpConfiguration config)
    19         {
    20             // Web API 配置和服务
    21             config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); //json格式化小驼峰
    22             config.Formatters.Remove(config.Formatters.XmlFormatter);// 取消XML返回格式
    23 
    24             // 使用自定义ControllerSelector,ActionSelector
    25             config.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector(config));
    26             config.Services.Replace(typeof(IHttpActionSelector), new CustomActionSelector());
    27 
    28             // 特性路由(RouteAttribute)
    29             config.MapHttpAttributeRoutes();
    30 
    31             // Action路由
    32             config.Routes.MapHttpRoute(
    33                 name: "ActionApi",
    34                 routeTemplate: "api/{controller}/{action}/{id}",
    35                 defaults: new { controller = "Common", action = "GetHttpCode", id = RouteParameter.Optional }
    36                 );
    37 
    38             // WebIndex路由,返回前端资源
    39             config.Routes.MapHttpRoute(
    40                 name: "WebIndex",
    41                 routeTemplate: "fe/{*.}",//catch-all
    42                 defaults: new { controller = "Web", action = "Index" }
    43                 );
    44         }
    45     }
    46 }

    这样只要请求URL中包含 /fe/ 都会返回Web/Index响应输出

    参考

    https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.2#multiple-routes

    https://www.cnblogs.com/Code-life/p/7182558.html

     
  • 相关阅读:
    慎用rm -rf
    Jquery 中a||""的含义
    【学习、总结】Spring security 登陆超时处理
    Eclipse 无限编译Invoking 'Maven Project Builder'导致卡主
    For多重循环 break continue
    随机编码的生成
    QQ互联Oauth2.0认证测试
    Java开发工程师(Web方向)
    Java开发工程师(Web方向)
    前端开发工程师
  • 原文地址:https://www.cnblogs.com/zhangyuan/p/10670801.html
Copyright © 2020-2023  润新知