【答客問】ASP.NET MVC ActionFilter 如何取得 RedirectToAction 路徑?
0 |
網友發問,ASP.NET MVC ActionFilter 可攔截檢查 MVC Action 執行結果,若 Action 傳回 return RedirectToAction("..."),ActionFilter 能否取得導向的 URL?
以下列程式為例,目標是在 TestActionFilter 取得 "/Home/Redirected":
using System.Web.Mvc;
using WebApplication1.Models;
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
public ActionResult Redirected()
{
return Content("Redirected");
}
[TestActionFilter]
public ActionResult Redirect()
{
return RedirectToAction("Redirected");
}
}
}
依 ActionFilter 運作原理,覆寫 OnActionExecuted() 後可由 ActionExecutedContext 的 Result 屬性取得 Action return 傳回內容。return RedirectToAtion() 時傳回型別為 RedirectToRouteResult,因此第一步是在 ActionFilter 加入邏輯,偵測 Result 型別為 RedirectToRouteResult 進行特別處理。RedirectToRouteResult 與 URL 路徑有關的屬性為 RouteName 及 RouteValues,土法鍊鋼自己推算是種解法,但依循程式原有邏輯更穩當。於是我開了 JustDecompile 反組譯觀察 RedirectToRouteResult.ExecuteResult() 方法:
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (context.IsChildAction)
{
throw new InvalidOperationException(MvcResources.RedirectAction_CannotRedirectInChildAction);
}
string str = UrlHelper.GenerateUrl(this.RouteName, null, null, this.RouteValues, this.Routes, context.RequestContext, false);
if (string.IsNullOrEmpty(str))
{
throw new InvalidOperationException(MvcResources.Common_NoRouteMatched);
}
context.Controller.TempData.Keep();
if (this.Permanent)
{
context.HttpContext.Response.RedirectPermanent(str, false);
return;
}
context.HttpContext.Response.Redirect(str, false);
}
internal RouteCollection Routes
{
get
{
if (this._routes == null)
{
this._routes = RouteTable.Routes;
}
return this._routes;
}
set
{
this._routes = value;
}
}
由以上程式碼得知 ExecuteResult() 內部是用 UrlHelper.GenerateUrl() 將 RouteName、RouteValues 轉成 URL,但還需要 this.Routes、context.RequestContext 兩個額外參數;ActionExecutedContext 也有 RequestContext,但 Routes 是 RedirectToRouteResult 的內部屬性無法動用,所幸再追進去發現 Routes 的來源是 RouteTable.Routes 為公開靜態屬性,這樣子要模擬 RedirectToRouteResult 計算 URL 就沒問題了。
綜合以上分析,最後 ActionFilter 會像這樣:
using NLog;
using System.Web.Mvc;
using System.Web.Routing;
namespace WebApplication1.Models
{
public class TestActionFilterAttribute : ActionFilterAttribute
{
static ILogger logger = NLog.LogManager.GetLogger("Trace");
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//由Result取出Action回傳結果
var result = filterContext.Result;
//檢查是否型別為 RediretToRouteResult
if (result is RedirectToRouteResult)
{
var redirRouteRes = result as RedirectToRouteResult;
//使用 UrlHelper 依 RedirectToRouteResult.RouteValues 計算出 URL
string url = UrlHelper.GenerateUrl(
redirRouteRes.RouteName, null, null, redirRouteRes.RouteValues,
RouteTable.Routes, filterContext.RequestContext, false);
logger.Trace($"RedirectToRoute: {url}");
}
base.OnActionExecuted(filterContext);
}
}
}
執行 /Home/Redriect 後得到 NLlog 記錄如下,測試成功:
2019-10-13 11:12:46.7169 TRACE RedirectToRoute: /Home/Redirected
假日暖身伸展完畢!
Example of how to read RedirectToAtion url from ActionFilter in ASP.NET MVC
Comments
Be the first to post a comment