Cách đặt Tuyến đường Mặc định (Đến một Khu vực) trong MVC


122

Ok điều này đã được hỏi trước đây nhưng không có giải pháp rắn ngoài đó. Vì vậy, vì mục đích của bản thân tôi và những người có thể thấy điều này hữu ích.

Trong MVC2 (ASP.NET), tôi muốn nó để khi ai đó điều hướng đến trang web, sẽ có một khu vực mặc định được chỉ định. Vì vậy, điều hướng đến trang web của tôi sẽ đưa bạn đến ControllerX ActionY trong AreaZ.

Sử dụng tuyến đường sau trong Global.asax

routes.MapRoute(
                "Area",
                "",
                new { area = "AreaZ", controller = "ControllerX ", action = "ActionY " }
            );

Bây giờ điều này hoạt động như trong nó cố gắng cung cấp trang chính xác. Tuy nhiên MVC tiến hành tìm View trong thư mục gốc của trang web chứ không phải trong thư mục Area.

Có cách nào để giải quyết điều này?

BIÊN TẬP

Có một 'Giải pháp' và đó là trong ControllerX, ActionY trả về đường dẫn đầy đủ của chế độ xem. Một chút hack nhưng nó hoạt động. Tuy nhiên, tôi hy vọng có một giải pháp tốt hơn.

         public ActionResult ActionY()
        {
            return View("~/Areas/AreaZ/views/ActionY.aspx");
        }

Biên tập:

Điều này cũng trở thành một vấn đề khi có một Liên kết hành động HTML của trang. Nếu khu vực không được đặt, Liên kết Hành động sẽ trống.

Tất cả những điều này là do thiết kế hay một lỗ hổng?

Câu trả lời:


98

Điều này khiến tôi quan tâm, và cuối cùng tôi đã có cơ hội xem xét nó. Những người khác dường như không hiểu rằng đây là vấn đề với việc tìm kiếm chế độ xem , không phải là vấn đề với chính việc định tuyến - và đó có thể là do tiêu đề câu hỏi của bạn chỉ ra rằng đó là về định tuyến.

Trong mọi trường hợp, vì đây là vấn đề liên quan đến Chế độ xem, cách duy nhất để đạt được những gì bạn muốn là ghi đè công cụ chế độ xem mặc định . Thông thường, khi bạn làm điều này, mục đích đơn giản là chuyển đổi chế độ xem của bạn (tức là sang Spark, NHaml, v.v.). Trong trường hợp này, nó không phải là logic tạo View mà chúng ta cần ghi đè, mà là FindPartialViewFindViewcác phương thức trong VirtualPathProviderViewEnginelớp.

Bạn có thể cảm ơn những ngôi sao may mắn của mình rằng những phương pháp này trên thực tế là ảo, bởi vì mọi thứ khác trong đó VirtualPathProviderViewEnginethậm chí không thể truy cập được - nó riêng tư và điều đó khiến bạn rất khó chịu khi ghi đè logic tìm kiếm vì về cơ bản bạn phải viết lại một nửa đoạn mã đã có được viết nếu bạn muốn nó hoạt động tốt với bộ đệm ẩn vị trí và các định dạng vị trí. Sau khi đào sâu trong Reflector, cuối cùng tôi đã tìm ra một giải pháp hiệu quả.

Những gì tôi đã làm ở đây là đầu tiên tạo một bản tóm tắt AreaAwareViewEnginedẫn xuất trực tiếp từ VirtualPathProviderViewEnginenó thay vì WebFormViewEngine. Tôi đã làm điều này để thay thế nếu bạn muốn tạo các khung nhìn Spark (hoặc bất cứ thứ gì), bạn vẫn có thể sử dụng lớp này làm loại cơ sở.

Đoạn mã dưới đây khá dài dòng, vì vậy, để cung cấp cho bạn một bản tóm tắt nhanh về những gì nó thực sự hoạt động: Nó cho phép bạn đặt một {2}định dạng vị trí, tương ứng với tên khu vực, theo cách {1}tương tự với tên bộ điều khiển. Đó là nó! Đó là những gì chúng tôi phải viết tất cả mã này cho:

BaseAreaAwareViewEngine.cs

public abstract class BaseAreaAwareViewEngine : VirtualPathProviderViewEngine
{
    private static readonly string[] EmptyLocations = { };

    public override ViewEngineResult FindView(
        ControllerContext controllerContext, string viewName,
        string masterName, bool useCache)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(viewName))
        {
            throw new ArgumentNullException(viewName,
                "Value cannot be null or empty.");
        }

        string area = getArea(controllerContext);
        return FindAreaView(controllerContext, area, viewName,
            masterName, useCache);
    }

    public override ViewEngineResult FindPartialView(
        ControllerContext controllerContext, string partialViewName,
        bool useCache)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(partialViewName))
        {
            throw new ArgumentNullException(partialViewName,
                "Value cannot be null or empty.");
        }

        string area = getArea(controllerContext);
        return FindAreaPartialView(controllerContext, area,
            partialViewName, useCache);
    }

    protected virtual ViewEngineResult FindAreaView(
        ControllerContext controllerContext, string areaName, string viewName,
        string masterName, bool useCache)
    {
        string controllerName =
            controllerContext.RouteData.GetRequiredString("controller");
        string[] searchedViewPaths;
        string viewPath = GetPath(controllerContext, ViewLocationFormats,
            "ViewLocationFormats", viewName, controllerName, areaName, "View",
            useCache, out searchedViewPaths);
        string[] searchedMasterPaths;
        string masterPath = GetPath(controllerContext, MasterLocationFormats,
            "MasterLocationFormats", masterName, controllerName, areaName,
            "Master", useCache, out searchedMasterPaths);
        if (!string.IsNullOrEmpty(viewPath) &&
            (!string.IsNullOrEmpty(masterPath) || 
              string.IsNullOrEmpty(masterName)))
        {
            return new ViewEngineResult(CreateView(controllerContext, viewPath,
                masterPath), this);
        }
        return new ViewEngineResult(
            searchedViewPaths.Union<string>(searchedMasterPaths));
    }

    protected virtual ViewEngineResult FindAreaPartialView(
        ControllerContext controllerContext, string areaName,
        string viewName, bool useCache)
    {
        string controllerName =
            controllerContext.RouteData.GetRequiredString("controller");
        string[] searchedViewPaths;
        string partialViewPath = GetPath(controllerContext,
            ViewLocationFormats, "PartialViewLocationFormats", viewName,
            controllerName, areaName, "Partial", useCache,
            out searchedViewPaths);
        if (!string.IsNullOrEmpty(partialViewPath))
        {
            return new ViewEngineResult(CreatePartialView(controllerContext,
                partialViewPath), this);
        }
        return new ViewEngineResult(searchedViewPaths);
    }

    protected string CreateCacheKey(string prefix, string name,
        string controller, string area)
    {
        return string.Format(CultureInfo.InvariantCulture,
            ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:",
            base.GetType().AssemblyQualifiedName,
            prefix, name, controller, area);
    }

    protected string GetPath(ControllerContext controllerContext,
        string[] locations, string locationsPropertyName, string name,
        string controllerName, string areaName, string cacheKeyPrefix,
        bool useCache, out string[] searchedLocations)
    {
        searchedLocations = EmptyLocations;
        if (string.IsNullOrEmpty(name))
        {
            return string.Empty;
        }
        if ((locations == null) || (locations.Length == 0))
        {
            throw new InvalidOperationException(string.Format("The property " +
                "'{0}' cannot be null or empty.", locationsPropertyName));
        }
        bool isSpecificPath = IsSpecificPath(name);
        string key = CreateCacheKey(cacheKeyPrefix, name,
            isSpecificPath ? string.Empty : controllerName,
            isSpecificPath ? string.Empty : areaName);
        if (useCache)
        {
            string viewLocation = ViewLocationCache.GetViewLocation(
                controllerContext.HttpContext, key);
            if (viewLocation != null)
            {
                return viewLocation;
            }
        }
        if (!isSpecificPath)
        {
            return GetPathFromGeneralName(controllerContext, locations, name,
                controllerName, areaName, key, ref searchedLocations);
        }
        return GetPathFromSpecificName(controllerContext, name, key,
            ref searchedLocations);
    }

    protected string GetPathFromGeneralName(ControllerContext controllerContext,
        string[] locations, string name, string controllerName,
        string areaName, string cacheKey, ref string[] searchedLocations)
    {
        string virtualPath = string.Empty;
        searchedLocations = new string[locations.Length];
        for (int i = 0; i < locations.Length; i++)
        {
            if (string.IsNullOrEmpty(areaName) && locations[i].Contains("{2}"))
            {
                continue;
            }
            string testPath = string.Format(CultureInfo.InvariantCulture,
                locations[i], name, controllerName, areaName);
            if (FileExists(controllerContext, testPath))
            {
                searchedLocations = EmptyLocations;
                virtualPath = testPath;
                ViewLocationCache.InsertViewLocation(
                    controllerContext.HttpContext, cacheKey, virtualPath);
                return virtualPath;
            }
            searchedLocations[i] = testPath;
        }
        return virtualPath;
    }

    protected string GetPathFromSpecificName(
        ControllerContext controllerContext, string name, string cacheKey,
        ref string[] searchedLocations)
    {
        string virtualPath = name;
        if (!FileExists(controllerContext, name))
        {
            virtualPath = string.Empty;
            searchedLocations = new string[] { name };
        }
        ViewLocationCache.InsertViewLocation(controllerContext.HttpContext,
            cacheKey, virtualPath);
        return virtualPath;
    }


    protected string getArea(ControllerContext controllerContext)
    {
        // First try to get area from a RouteValue override, like one specified in the Defaults arg to a Route.
        object areaO;
        controllerContext.RouteData.Values.TryGetValue("area", out areaO);

        // If not specified, try to get it from the Controller's namespace
        if (areaO != null)
            return (string)areaO;

        string namespa = controllerContext.Controller.GetType().Namespace;
        int areaStart = namespa.IndexOf("Areas.");
        if (areaStart == -1)
            return null;

        areaStart += 6;
        int areaEnd = namespa.IndexOf('.', areaStart + 1);
        string area = namespa.Substring(areaStart, areaEnd - areaStart);
        return area;
    }

    protected static bool IsSpecificPath(string name)
    {
        char ch = name[0];
        if (ch != '~')
        {
            return (ch == '/');
        }
        return true;
    }
}

Như đã nói, đây không phải là một động cơ cụ thể, vì vậy bạn cũng phải tạo ra nó. May mắn thay, phần này dễ dàng hơn nhiều , tất cả những gì chúng ta cần làm là đặt các định dạng mặc định và thực sự tạo các chế độ xem:

AreaAwareViewEngine.cs

public class AreaAwareViewEngine : BaseAreaAwareViewEngine
{
    public AreaAwareViewEngine()
    {
        MasterLocationFormats = new string[]
        {
            "~/Areas/{2}/Views/{1}/{0}.master",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.master",
            "~/Areas/{2}/Views/Shared/{0}.cshtml",
            "~/Views/{1}/{0}.master",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.master"
            "~/Views/Shared/{0}.cshtml"
        };
        ViewLocationFormats = new string[]
        {
            "~/Areas/{2}/Views/{1}/{0}.aspx",
            "~/Areas/{2}/Views/{1}/{0}.ascx",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.aspx",
            "~/Areas/{2}/Views/Shared/{0}.ascx",
            "~/Areas/{2}/Views/Shared/{0}.cshtml",
            "~/Views/{1}/{0}.aspx",
            "~/Views/{1}/{0}.ascx",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.aspx"
            "~/Views/Shared/{0}.ascx"
            "~/Views/Shared/{0}.cshtml"
        };
        PartialViewLocationFormats = ViewLocationFormats;
    }

    protected override IView CreatePartialView(
        ControllerContext controllerContext, string partialPath)
    {
        if (partialPath.EndsWith(".cshtml"))
            return new System.Web.Mvc.RazorView(controllerContext, partialPath, null, false, null);
        else
            return new WebFormView(controllerContext, partialPath);
    }

    protected override IView CreateView(ControllerContext controllerContext,
        string viewPath, string masterPath)
    {
        if (viewPath.EndsWith(".cshtml"))
            return new RazorView(controllerContext, viewPath, masterPath, false, null);
        else
            return new WebFormView(controllerContext, viewPath, masterPath);
    }
}

Lưu ý rằng chúng tôi đã thêm một số mục vào tiêu chuẩn ViewLocationFormats. Đây là các {2}mục nhập mới , nơi mục {2}sẽ được ánh xạ tới mục areachúng tôi đưa vào RouteData. Tôi đã để MasterLocationFormatsyên, nhưng rõ ràng bạn có thể thay đổi điều đó nếu bạn muốn.

Bây giờ sửa đổi của bạn global.asaxđể đăng ký công cụ xem này:

Global.asax.cs

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new AreaAwareViewEngine());
}

... và đăng ký tuyến đường mặc định:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        "Area",
        "",
        new { area = "AreaZ", controller = "Default", action = "ActionY" }
    );
    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = "" }
    );
}

Bây giờ Tạo AreaControllerchúng tôi vừa tham chiếu:

DefaultController.cs (trong ~ / Controllers /)

public class DefaultController : Controller
{
    public ActionResult ActionY()
    {
        return View("TestView");
    }
}

Rõ ràng là chúng ta cần cấu trúc thư mục và chế độ xem đi cùng với nó - chúng ta sẽ giữ điều này cực kỳ đơn giản:

TestView.aspx (trong ~ / Areas / AreaZ / Views / Default / hoặc ~ / Areas / AreaZ / Views / Shared /)

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<h2>TestView</h2>
This is a test view in AreaZ.

Và đó là nó. Cuối cùng, chúng tôi đã hoàn thành .

Đối với hầu hết các phần, bạn sẽ có thể chỉ cần đi BaseAreaAwareViewEngineAreaAwareViewEnginevà thả nó vào bất kỳ dự án MVC, vì vậy mặc dù phải mất rất nhiều mã để thực hiện điều này, bạn chỉ phải viết nó một lần. Sau đó, bạn chỉ cần chỉnh sửa một vài dòng global.asax.csvà tạo cấu trúc trang web của mình.


Đây là giải pháp tốt nhất hiện tại nhưng không lý tưởng lắm. Như trên khi bạn thêm một Actionlink hoặc vấn đề tương tự vẫn tồn tại.
LiamB

1
@Pino: Tôi nghĩ rằng bạn sẽ có thể giải quyết ActionLinkvấn đề bằng cách thêm điều tương tự area = "AreaZ"vào ánh xạ tuyến đường "Mặc định" trong global.asax.cs. Tôi không tích cực mặc dù; hãy thử nó và xem.
Aaronaught

Trong MVC4 khai báo tuyến đường "Mặc định" đã chuyển từ Global.asax sang ~ / App_Start / RouteConfig.cs / RegisterRoutes ()
Andriy F.

3
Tôi ghét phản đối, nhưng tôi thực sự không thể tin được câu trả lời dưới đây của @Chris Alderson đã không nhận được nhiều phiếu bầu hơn. Đó là một nhiều giải pháp đơn giản hơn một này và dường như để giải quyết các trường hợp cạnh (ActionLinks, vv).
jdmcnair

Có vẻ như có một lỗi ở đây. Ví dụ: Chế độ xem cho một Khu vực có tên "Re" sẽ nằm trong ~ / Areas / Re / Views / Ctrlr / blah.aspx, nhưng mã ở đây sử dụng ~ / {2} / {1} / {0} sẽ là ~ /Re/Ctrl/blah.aspx, thiếu thư mục Khu vực quan trọng trong đường dẫn. Nó phải là "~ / Areas / {2} / Views / {1} / {0} .aspx"
Chris Moschini

100

Đây là cách tôi đã làm điều đó. Tôi không biết tại sao MapRoute () không cho phép bạn thiết lập khu vực, nhưng nó trả về đối tượng tuyến đường để bạn có thể tiếp tục thực hiện bất kỳ thay đổi bổ sung nào bạn muốn. Tôi sử dụng điều này vì tôi có một trang web MVC mô-đun được bán cho khách hàng doanh nghiệp và họ cần có thể thả dlls vào thư mục bin để thêm mô-đun mới. Tôi cho phép họ thay đổi "HomeArea" trong cấu hình AppSettings.

var route = routes.MapRoute(
                "Home_Default", 
                "", 
                new {controller = "Home", action = "index" },
                new[] { "IPC.Web.Core.Controllers" }
               );
route.DataTokens["area"] = area;

Chỉnh sửa: Bạn cũng có thể thử điều này trong AreaRegistration.RegisterArea cho khu vực bạn muốn người dùng truy cập theo mặc định. Tôi chưa thử nghiệm nó nhưng AreaRegistrationContext.MapRoute thiết lập route.DataTokens["area"] = this.AreaName;cho bạn.

context.MapRoute(
                    "Home_Default", 
                    "", 
                    new {controller = "Home", action = "index" },
                    new[] { "IPC.Web.Core.Controllers" }
                   );

Nó hoạt động. Hãy coi chừng tệp web.config mới, nó có thể ghi đè các cấu hình chung cũ của bạn.
Mert Akcakaya

56

ngay cả khi nó đã được trả lời - đây là cú pháp ngắn (ASP.net 3, 4, 5):

routes.MapRoute("redirect all other requests", "{*url}",
    new {
        controller = "UnderConstruction",
        action = "Index"
        }).DataTokens = new RouteValueDictionary(new { area = "Shop" });

6
Điều này làm việc tuyệt vời cho tôi. Tôi không có bất kỳ bộ điều khiển nào ở gốc và chỉ sử dụng Khu vực. Đối với MVC 4, tôi có điều này thay thế mặc định trong RouteConfig.cs. Cảm ơn!
Marc

2
Tôi đang sử dụng MVC4 và đây là giải pháp đơn giản nhất cho tôi. Cho phép ứng dụng sử dụng chế độ xem Chỉ mục trong một Khu vực cụ thể làm 'trang chủ' của trang web.
JTech

2
Giải pháp này sẽ không hoạt động trong tương lai (từ Asp.Net MVC6 trở lên).
Patrick Desjardins

@PatrickDesjardins: Bất kỳ lý do gì không hỗ trợ giải pháp trên?
Akash KC

@SeriousM Bạn trả lời là thường xanh. Nó vẫn hữu ích. Bạn đã cứu tôi một đêm.
skpaul

16

Cảm ơn Aaron đã chỉ ra rằng đó là việc xác định các quan điểm, tôi đã hiểu sai điều đó.

[CẬP NHẬT] Tôi vừa tạo một dự án đưa người dùng đến một Khu vực theo mặc định mà không gây rắc rối với bất kỳ mã hoặc đường dẫn tra cứu nào:

Trong global.asax, đăng ký như bình thường:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default",                                              // Route name
            "{controller}/{action}/{id}",                           // URL with parameters
            new { controller = "Home", action = "Index", id = ""}  // Parameter defaults,
        );
    }

trong Application_Start(), hãy đảm bảo sử dụng thứ tự sau;

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }

trong khu vực của bạn đăng ký, sử dụng

    public override void RegisterArea(AreaRegistrationContext context)
    {
        context.MapRoute(
            "ShopArea_default",
            "{controller}/{action}/{id}",
            new { action = "Index", id = "", controller = "MyRoute" },
            new { controller = "MyRoute" }
        );
    }

Có thể tìm thấy một ví dụ tại http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/

Tôi thực sự hy vọng rằng đây là những gì bạn đang yêu cầu ...

////

Tôi không nghĩ rằng viết giả ViewEnginelà giải pháp tốt nhất trong trường hợp này. (Thiếu uy tín, tôi không thể bình luận). Các WebFormsViewEnginelà Khu vực nhận thức và chứa AreaViewLocationFormatsđược định nghĩa mỗi mặc định như

AreaViewLocationFormats = new[] {
        "~/Areas/{2}/Views/{1}/{0}.aspx",
        "~/Areas/{2}/Views/{1}/{0}.ascx",
        "~/Areas/{2}/Views/Shared/{0}.aspx",
        "~/Areas/{2}/Views/Shared/{0}.ascx",
    };

Tôi tin rằng bạn không tuân thủ quy ước này. Bạn đã đăng

public ActionResult ActionY() 
{ 
    return View("~/Areas/AreaZ/views/ActionY.aspx"); 
} 

như một bản hack đang hoạt động, nhưng điều đó phải

   return View("~/Areas/AreaZ/views/ControllerX/ActionY.aspx"); 

Tuy nhiên, NẾU bạn không muốn tuân theo quy ước, bạn có thể muốn đi một con đường ngắn bằng cách lấy từ WebFormViewEngine(ví dụ: được thực hiện trong MvcContrib), nơi bạn có thể đặt các đường dẫn tra cứu trong hàm tạo, hoặc -a một chút hacky- bằng cách chỉ định quy ước của bạn như thế này trên Application_Start:

((VirtualPathProviderViewEngine)ViewEngines.Engines[0]).AreaViewLocationFormats = ...;

Tất nhiên, điều này nên được thực hiện cẩn thận hơn một chút, nhưng tôi nghĩ nó cho thấy ý tưởng. Những lĩnh vực đang publicVirtualPathProviderViewEnginetrong MVC 2 RC.


Cần lưu ý rằng điều này chỉ áp dụng trong MVC 2 RC - MVC 1 VirtualPathProviderViewEnginekhông có thuộc tính này và không nhận biết khu vực. Và trong khi câu hỏi này thực sự được nêu là về MVC 2, rất nhiều người vẫn chưa sử dụng nó (và sẽ không còn nữa). Vì vậy, câu trả lời của bạn dễ dàng hơn cho câu hỏi cụ thể, nhưng câu trả lời của tôi là câu duy nhất phù hợp với những người dùng MVC1 vấp phải câu hỏi này. Tôi muốn cung cấp các câu trả lời không phụ thuộc vào chức năng tiền phát hành có khả năng thay đổi.
Aaronaught

Ngoài ra, nó không phải là "công cụ chế độ xem giả" - các lớp công cụ chế độ xem đã được cố tình tạo ra để có thể mở rộng để có thể sử dụng các loại chế độ xem khác nhau.
Aaronaught

Điều đó không nhằm mục đích xúc phạm bạn, tôi xin lỗi. Nó là 'giả' ở chỗ nó không thay đổi đáng kể cách Chế độ xem được xử lý, mà chỉ thay thế một số giá trị.
mnemosyn

Tôi không bị xúc phạm, tôi chỉ muốn làm rõ sự thật rằng đó không phải là lý do đặc biệt bất thường để tạo ra một công cụ chế độ xem tùy chỉnh, bằng chứng là các phương thức liên quan có thể bị ghi đè.
Aaronaught

2
Mẹo hay về việc RegisterAreasđi trước RegisterRoutes. Tôi đang tự hỏi tại sao mã của tôi đột nhiên ngừng hoạt động và nhận thấy bộ tái cấu trúc đó;)
webnoob

6

Tôi đoán bạn muốn người dùng được chuyển hướng đến ~/AreaZURL sau khi (các) người đó đã truy cập ~/URL. Tôi sẽ đạt được bằng mã sau trong thư mục gốc của bạn HomeController.

public class HomeController
{
    public ActionResult Index()
    {
        return RedirectToAction("ActionY", "ControllerX", new { Area = "AreaZ" });
    }
}

Và các tuyến đường sau đây trong Global.asax.

routes.MapRoute(
    "Redirection to AreaZ",
    String.Empty,
    new { controller = "Home ", action = "Index" }
);

Điều này hoạt động, nhưng nó thay đổi thành URL trên trình duyệt của người dùng. Không thực sự lý tưởng.
LiamB

2

Đầu tiên, bạn đang sử dụng phiên bản MVC2 nào? Đã có những thay đổi đáng kể từ preview2 sang RC.

Giả sử bạn sử dụng RC, tôi nghĩ bạn lập bản đồ tuyến đường sẽ trông khác. Trong AreaRegistration.cskhu vực của bạn, bạn có thể đăng ký một số loại tuyến đường mặc định, ví dụ:

        context.MapRoute(
            "ShopArea_default",
            "{controller}/{action}/{id}",
            new { action = "Index", id = "", controller="MyRoute" }
        );

Đoạn mã trên sẽ gửi người dùng đến theo mặc định MyRouteControllercủa chúng tôi ShopArea.

Việc sử dụng một chuỗi trống làm tham số thứ hai sẽ tạo ra một ngoại lệ, vì một bộ điều khiển phải được chỉ định.

Tất nhiên, bạn sẽ phải thay đổi tuyến đường mặc định Global.asaxđể nó không ảnh hưởng đến tuyến đường mặc định này, ví dụ: bằng cách sử dụng tiền tố cho trang web chính.

Cũng xem chủ đề này và câu trả lời của Haack: Thứ tự tuyến đường đăng ký MVC 2

Hi vọng điêu nay co ich.


Cảm ơn, nhưng tôi không chắc điều này giải quyết được vấn đề được giải thích trong câu hỏi. Và MVC RC của nó
LiamB

2

Việc thêm phần sau vào Application_Start của tôi phù hợp với tôi, mặc dù tôi không chắc liệu bạn có cài đặt này trong RC hay không:

var engine = (WebFormViewEngine)ViewEngines.Engines.First();

// These additions allow me to route default requests for "/" to the home area
engine.ViewLocationFormats = new string[] { 
    "~/Views/{1}/{0}.aspx",
    "~/Views/{1}/{0}.ascx",
    "~/Areas/{1}/Views/{1}/{0}.aspx", // new
    "~/Areas/{1}/Views/{1}/{0}.ascx", // new
    "~/Areas/{1}/Views/{0}.aspx", // new
    "~/Areas/{1}/Views/{0}.ascx", // new
    "~/Views/{1}/{0}.ascx",
    "~/Views/Shared/{0}.aspx",
    "~/Views/Shared/{0}.ascx"
};

1

Những gì tôi đã làm để làm cho nó hoạt động như sau:

  1. Tôi đã tạo bộ điều khiển mặc định trong thư mục gốc / Bộ điều khiển. Tôi đặt tên bộ điều khiển của mình là DefaultController.
  2. Trong bộ điều khiển, tôi đã thêm mã sau:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
  3. Trong RouterConfig.cs của tôi, tôi đã thêm những thứ sau:

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new {controller = "Default", action = "Index", id = UrlParameter.Optional});

Bí quyết đằng sau tất cả những điều này là tôi đã tạo một hàm tạo mặc định sẽ luôn là bộ điều khiển khởi động mỗi khi ứng dụng của tôi khởi động. Khi nó chạm vào bộ điều khiển mặc định đó, nó sẽ chuyển hướng đến bất kỳ bộ điều khiển nào mà tôi chỉ định trong Hành động chỉ mục mặc định. Mà trong trường hợp của tôi là

www.myurl.com/FolderName/ControllerName

.


0
routes.MapRoute(
                "Area",
                "{area}/",
                new { area = "AreaZ", controller = "ControlerX ", action = "ActionY " }
            );

Bạn đã thử điều đó chưa?


Có, vấn đề là do bây giờ trang web tìm kiếm các chế độ xem trong thư mục gốc. Không tìm thấy chế độ xem 'ActionY' hoặc chế độ xem chính của nó. Các vị trí sau được tìm kiếm: ~ / Views / ActionY / ActionY.aspx ~ / Views / ActionY / ActionY.ascx ~ / Views / Shared / ActionY.aspx ~ / Views / Shared / ActionY.ascx
LiamB

2
Tôi hiểu. Tôi sẽ cố gắng tìm ra một giải pháp. 1 cho câu hỏi
Barbaros Alp

0

Định vị các khối xây dựng khác nhau được thực hiện trong vòng đời yêu cầu. Một trong những bước đầu tiên trong vòng đời yêu cầu ASP.NET MVC là ánh xạ URL được yêu cầu tới phương thức hành động của bộ điều khiển chính xác. Quá trình này được gọi là định tuyến. Một tuyến mặc định được khởi tạo trong tệp Global.asax và mô tả cho khung ASP.NET MVC cách xử lý một yêu cầu. Nhấp đúp vào tệp Global.asax trong dự án MvcApplication1 sẽ hiển thị mã sau:

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

namespace MvcApplication1 {

   public class GlobalApplication : System.Web.HttpApplication
   {
       public static void RegisterRoutes(RouteCollection routes)
       {
           routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

           routes.MapRoute(
               "Default",                                          // Route name
               "{controller}/{action}/{id}",                       // URL with parameters
               new { controller = "Home", action = "Index",
                     id = "" }  // Parameter defaults
           );

       }

       protected void Application_Start()
       {
           RegisterRoutes(RouteTable.Routes);
       }
   }

}

Trong trình xử lý sự kiện Application_Start (), được kích hoạt bất cứ khi nào ứng dụng được biên dịch hoặc máy chủ web được khởi động lại, một bảng lộ trình được đăng ký. Tuyến đường mặc định được đặt tên là Mặc định và phản hồi một URL có dạng http://www.example.com/ {controller} / {action} / {id}. Các biến giữa {và} được điền bằng các giá trị thực tế từ URL yêu cầu hoặc bằng các giá trị mặc định nếu không có ghi đè nào trong URL. Lộ trình mặc định này sẽ ánh xạ tới Bộ điều khiển chính và phương thức hành động Chỉ mục, theo các tham số định tuyến mặc định. Chúng tôi sẽ không có bất kỳ hành động nào khác với bản đồ định tuyến này.

Theo mặc định, tất cả các URL có thể được ánh xạ thông qua tuyến đường mặc định này. Cũng có thể tạo các tuyến đường của riêng chúng tôi. Ví dụ: hãy ánh xạ URL http://www.example.com/Eaffee/Maarten với bộ điều khiển Nhân viên, hành động Hiển thị và tham số tên đầu tiên. Đoạn mã sau có thể được chèn vào tệp Global.asax mà chúng tôi vừa mở. Vì khung ASP.NET MVC sử dụng tuyến đường phù hợp đầu tiên, đoạn mã này phải được chèn phía trên tuyến đường mặc định; nếu không tuyến đường sẽ không bao giờ được sử dụng.

routes.MapRoute(

   "EmployeeShow",                    // Route name
   "Employee/{firstname}",            // URL with parameters
    new {                             // Parameter defaults
       controller = "Employee",
       action = "Show", 
       firstname = "" 
   }  

);

Bây giờ, hãy thêm các thành phần cần thiết cho tuyến đường này. Trước hết, tạo một lớp có tên EmployeeController trong thư mục Controllers. Bạn có thể thực hiện việc này bằng cách thêm một mục mới vào dự án và chọn mẫu Lớp điều khiển MVC nằm dưới Web | Danh mục MVC. Loại bỏ phương thức hành động Chỉ mục và thay thế nó bằng một phương thức hoặc hành động có tên Hiển thị. Phương thức này chấp nhận một tham số tên đầu tiên và chuyển dữ liệu vào từ điển ViewData. Từ điển này sẽ được chế độ xem sử dụng để hiển thị dữ liệu.

Lớp EmployeeController sẽ truyền một đối tượng Employee vào dạng xem. Lớp Nhân viên này sẽ được thêm vào thư mục Mô hình (nhấp chuột phải vào thư mục này, sau đó chọn Thêm | Lớp từ trình đơn ngữ cảnh). Đây là mã cho lớp Nhân viên:

namespace MvcApplication1.Models {

   public class Employee
   {
       public string FirstName { get; set; }
       public string LastName { get; set; }
       public string Email { get; set; }
   }

} 

1
Cảm ơn, tôi không chắc điều này liên quan như thế nào đến việc đặt VÙNG mặc định. : - /
LiamB

0

Chà, trong khi việc tạo công cụ chế độ xem tùy chỉnh có thể hoạt động cho việc này, bạn vẫn có thể có một giải pháp thay thế:

  • Quyết định những gì bạn cần hiển thị theo mặc định.
  • Cái gì đó có bộ điều khiển và hành động (và Khu vực), phải không?
  • Mở đăng ký Khu vực đó và thêm một cái gì đó như sau:
public override void RegisterArea(AreaRegistrationContext context)
{
    //this makes it work for the empty url (just domain) to act as current Area.
    context.MapRoute(
        "Area_empty",
        "",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        namespaces: new string[] { "Area controller namespace" }
    );
        //other routes of the area
}

Chúc mừng!


Đã đồng ý. Mặc dù tôi nghĩ rằng một nơi thích hợp hơn cho định nghĩa tuyến đường này là trong tệp Global.asax.
nuhusky2003

Trong trường hợp như vậy, các định nghĩa global.asax của bạn sẽ biết về sự tồn tại của vùng tên bộ điều khiển khu vực, điều này tôi nghĩ là không đúng. Các khu vực là một chức năng được bổ sung, có nghĩa là bạn phải có thể thêm / xóa một khu vực mà không cần chạm vào định nghĩa global.asax. Trong cách tiếp cận câu hỏi của tôi, tôi thích một khu vực để "tiếp nhận" yêu cầu, thay vì một trang web [toàn cầu] để "chuyển giao" yêu cầu.
Tengiz

0

Giải pháp được chấp nhận cho câu hỏi này là, trong khi tổng hợp chính xác về cách tạo công cụ chế độ xem tùy chỉnh, không trả lời chính xác câu hỏi. Vấn đề ở đây là Pino chỉ định sai tuyến đường mặc định của mình . Riêng định nghĩa "khu vực" của anh ấy là không chính xác. "Khu vực" được kiểm tra thông qua bộ sưu tập DataTokens và phải được thêm vào như vậy:

var defaultRoute = new Route("",new RouteValueDictionary(){{"controller","Default"},{"action","Index"}},null/*constraints*/,new RouteValueDictionary(){{"area","Admin"}},new MvcRouteHandler());
defaultRoute.DataTokens.Add("Namespaces","MyProject.Web.Admin.Controller"); 
routes.Add(defaultRoute);

"Khu vực" được chỉ định trong đối tượng mặc định sẽ bị bỏ qua . Mã trên tạo một tuyến mặc định, tuyến này bắt các yêu cầu tới thư mục gốc của trang web của bạn và sau đó gọi Bộ điều khiển mặc định, Hành động lập chỉ mục trong khu vực Quản trị. Cũng xin lưu ý rằng khóa "Không gian tên" đang được thêm vào DataTokens, điều này chỉ bắt buộc nếu bạn có nhiều bộ điều khiển có cùng tên. Giải pháp này được xác minh với Mvc2 và Mvc3 .NET 3.5 / 4.0


-1

ummm, tôi không biết tại sao tất cả chương trình này, tôi nghĩ rằng vấn đề ban đầu được giải quyết dễ dàng bằng cách chỉ định tuyến đường mặc định này ...

routes.MapRoute("Default", "{*id}", 
                 new { controller = "Home"
                     , action = "Index"
                     , id = UrlParameter.Optional 
                     }
              );
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.