01/10/2018, 17:32

Customize routing system của asp.net mvc

Nếu bạn không thích cách những đối tượng của lớp Route tìm ra những url khớp với nó hoặc bạn muốn làm một điều gì đó khác thường, bạn hãy làm điều đó bằng cách thừa kế từ lớp RouteBase. Điều này giúp cho chúng ta điều khiển được việc các url được khớp, các tham số được chiết ra từ url và các ...

Nếu bạn không thích cách những đối tượng của lớp Route tìm ra những url khớp với nó hoặc bạn muốn làm một điều gì đó khác thường, bạn hãy làm điều đó bằng cách thừa kế từ lớp RouteBase. Điều này giúp cho chúng ta điều khiển được việc các url được khớp, các tham số được chiết ra từ url và các outgoing url của chúng ta được tạo ra như thế nào

Khi chúng ta thừa kế từ lớp RouteBase, chúng ta cần implement 2 phương thức, đó là:
– GetRouteData(HttpContextBase httpContext): đây là cơ chế để cho việc tìm kiếm các url đi vào nào khớp với route. Framework gọi phương thức này trên mỗi route của RouteTable.Routes cho đến khi một giá trị không phải null được trả ra
– GetVirtualPath(RequestContext requestContext, RouteValueDictionary values): đây là cơ chế cho việc tạo các url đi ra. Framework gọi phương thức này trên mỗi route của RouteTable.Routes cho đến khi một đối tượng không phải là null được trả về.

I. Custom route cho việc gì?
Chúng ta sẽ mô phỏng chức năng mvc hỗ trợ chúng ta như sau. Giả sử website chúng ta có những liên kết cũ, những liên kết này thuộc mã nguồn mở của php như Joomla, WordPress nên có không có chạy trên asp.net mvc. Chúng ta muốn giữ cho những link làm việc với mã nguồn asp.net mvc của chúng ta thì chúng ta cần làm như sau

1. Thừa kế lớp RouteBase và override phương thức GetRouteData của nó
Chúng ta tạo thư mục có tên là Infrastructure, sau đó tạo ra một class có tên là LegacyRoute có đoạn mã như sau

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;

namespace GocKinhNghiem.Infrastructure
{
    public class LegacyRoute : RouteBase
    {
        private string[] urls;

        public LegacyRoute(params string[] targetUrls)
        {
            urls = targetUrls;
        }

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            RouteData result = null;

            string requestedURL =
                httpContext.Request.AppRelativeCurrentExecutionFilePath;
            if (urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
            {
                result = new RouteData(this, new MvcRouteHandler());
                result.Values.Add("controller", "Legacy");
                result.Values.Add("action", "GetLegacyURL");
                result.Values.Add("legacyURL", requestedURL);
            }
            return result;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext,
            RouteValueDictionary values)
        {

            return null;
        }
    }
}

Hàm khởi tạo của lớp LegacyRoute có một tham số targetUrls. Tham số này chúng ta dùng để truyền vào một tập hợp các url của hệ thống cũ mà chúng ta không dùng được trên hệ thống asp.net mvc mới của chúng ta

Lớp custom LegacyRoute của chúng ta có nhiệm vụ là chụp các incoming url. Sau khi LegacyRoute chụp được incoming url, nó mới tiến hành so sánh với với tập hợp các url cũ do chúng ta định nghĩa. Nếu incoming url có trong danh các url cũ thì nó tiến hành đưa vào action thích hợp để xử lý. Nếu icoming url khớp với một trong các url cũ của chúng ta, thì một đối tượng RouteData sẽ được trả về, đối tượng này chứa đựng những thông tin bao gồm action, controller và những thông tin đi kèm với action. Nếu incoming url không khớp với tập hợp url cũ của chúng ta thì sẽ trả về null để cho hệ thống route của chúng ta di chuyển đến route kế tiếp trong hệ thống

2. Đăng ký custom route
Sau khi chúng ta đã tạo LegacyRoute như ở trên, chúng ta mở file global.asax để đăng kí custom route của chúng ta. Ở đây, chúng ta tiến hành đăng ký custom route với vài liên kết cũ như sau:

routes.Add(new LegacyRoute(
        "~/articles/customize-routing-system-cua-asp-net-mvc/",
        "~/articles/tao-outgoing-url-trong-asp-net-mvc/"));

3. Tạo action xử lý các link cũ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace GocKinhNghiem.Controllers
{
    public class LegacyController : Controller
    {
        //
        // GET: /Legacy/

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult GetLegacyURL(string legacyURL)
        {
            return View((object)legacyURL);
        } 

    }
}

4. Tạo view
Chúng ta right click lên action GetLegacyURL, chọn Add View để tạo view mới với đoạn mã như sau

@model string 

@{
    ViewBag.Title = "GetLegacyURL";
Layout = null;
}

GetLegacyURL

The URL requested was: @Model

Kết quả chúng ta request những liên kết cũ sẽ như sau:

II. Tạo outgoing trong các custom route
Chúng ta đã nói ở trên là để tạo các outgoing url trong các custom route thì chúng ta cần override phương thức GetVirtualPath. Ở ví dụ phía trên thì chúng ta chưa làm gì cho phương thức GetVirtualPath ngoài việc đơn giản trả về một giá trị null. Chúng ta thêm một ít code vào lớp LegacyRoute mà chúng ta đã tạo ra ở trên như sau:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Web.Mvc;

namespace GocKinhNghiem.Infrastructure
{
    public class LegacyRoute : RouteBase
    {
        private string[] urls;

        public LegacyRoute(params string[] targetUrls)
        {
            urls = targetUrls;
        }

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            RouteData result = null;

            string requestedURL =
                httpContext.Request.AppRelativeCurrentExecutionFilePath;
            if (urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
            {
                result = new RouteData(this, new MvcRouteHandler());
                result.Values.Add("controller", "Legacy");
                result.Values.Add("action", "GetLegacyURL");
                result.Values.Add("legacyURL", requestedURL);
            }
            return result;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext,
            RouteValueDictionary values)
        {

            VirtualPathData result = null;

            if (values.ContainsKey("legacyURL") &&
                urls.Contains((string)values["legacyURL"], StringComparer.OrdinalIgnoreCase))
            {
                result = new VirtualPathData(this,
                    new UrlHelper(requestContext)
                        .Content((string)values["legacyURL"]).Substring(1));
            }
            return result;
        }
    }
}

Chúng ta truyền những biến segment và các chi tiết bằng cách dùng anonymous type. Tuy nhiên, hệ thống route đã âm thầm làm giúp chúng ta một việc là chuyển những giá trị đó vào RouteValueDictionary. Khi chúng ta viết một câu lệnh như sau vào view:

@Html.ActionLink("Click me", "GetLegacyURL", new { legacyURL =
    "~/articles/Windows_3.1_Overview.html" })

thì anonymous type với thuộc tính legacyURL sẽ được chuyển vào RouteValueDictonary với key cùng tên. Trong ví dụ của chúng ta, chúng ta sẽ xử lý những request cho các url đi ra ngoài nếu giá trị của nó nằm trong danh sách tập hợp url cũ của chúng ta

Nếu url có nằm trong danh sách url cũ của chúng ta, một đối tượng VirtualPathData sẽ được trả về, đi cùng với nó là tham chiếu đến đối tượng hiện tại và outbound url

III. Tạo custom route handler
Lớp MvcRouteHandler giúp chúng ta kết nối hệ thống routing với MVC framework. Chúng ta có thể định nghĩa các route handler theo ý chúng ta bằng cách implement IRouteHandler interface.

Chúng ta right click lên thư mục Infrastructute tạo file có tên là CustomRouteHandler.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;

namespace GocKinhNghiem.Infrastructure
{
    public class CustomRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new CustomHttpHandler();
        }
    }
    public class CustomHttpHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { return false; }
        }
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("Hello");
        }
    }
}

Chúng ta tiến hành đăng ký route để sử dụng custom route handler của chúng ta như sau:

 routes.Add(new Route("SayHello", new CustomRouteHandler()));

0