วิธีการจำลองเซิร์ฟเวอร์โอนใน ASP.NET MVC?


124

ใน ASP.NET MVC คุณสามารถส่งคืน ActionResult เปลี่ยนเส้นทางได้อย่างง่ายดาย:

 return RedirectToAction("Index");

 or

 return RedirectToRoute(new { controller = "home", version = Math.Random() * 10 });

สิ่งนี้จะให้การเปลี่ยนเส้นทาง HTTP ซึ่งปกติจะใช้ได้ อย่างไรก็ตามเมื่อใช้การวิเคราะห์ของ Google สิ่งนี้ทำให้เกิดปัญหาใหญ่เนื่องจากผู้อ้างอิงดั้งเดิมหายไป Google จึงไม่ทราบว่าคุณมาจากไหน ซึ่งจะสูญเสียข้อมูลที่เป็นประโยชน์เช่นคำศัพท์ของเครื่องมือค้นหา

หมายเหตุด้านข้างวิธีนี้มีข้อดีในการลบพารามิเตอร์ใด ๆ ที่อาจมาจากแคมเปญ แต่ยังช่วยให้ฉันสามารถจับภาพทางฝั่งเซิร์ฟเวอร์ได้ การปล่อยทิ้งไว้ในสตริงข้อความค้นหาจะทำให้มีคนบุ๊กมาร์กหรือทวิตเตอร์หรือบล็อกลิงก์ที่ไม่ควรทำ ฉันเคยเห็นสิ่งนี้หลายครั้งที่ผู้คนได้เปลี่ยนลิงก์ไปยังไซต์ของเราที่มีรหัสแคมเปญ

อย่างไรก็ตามฉันกำลังเขียนตัวควบคุม 'เกตเวย์' สำหรับการเข้าชมไซต์ที่เข้ามาทั้งหมดซึ่งฉันอาจเปลี่ยนเส้นทางไปยังสถานที่อื่นหรือเวอร์ชันอื่น

ตอนนี้ฉันสนใจ Google มากขึ้นในตอนนี้ (มากกว่าการบุ๊กมาร์กโดยไม่ตั้งใจ) และฉันต้องการส่งคนที่เยี่ยมชม/ไปยังหน้าที่พวกเขาจะได้รับหากพวกเขาไป/home/7ซึ่งเป็นหน้าแรกเวอร์ชัน 7

อย่างที่ฉันเคยพูดไปแล้วถ้าฉันทำสิ่งนี้ฉันจะสูญเสียความสามารถในการวิเคราะห์ผู้อ้างอิงของ Google:

 return RedirectToAction(new { controller = "home", version = 7 });

สิ่งที่ฉันต้องการจริงๆคือไฟล์

 return ServerTransferAction(new { controller = "home", version = 7 });

ซึ่งจะทำให้ฉันได้รับมุมมองนั้นโดยไม่มีการเปลี่ยนเส้นทางฝั่งไคลเอ็นต์ ฉันไม่คิดว่าจะมีสิ่งนี้อยู่จริง

ขณะนี้สิ่งที่ดีที่สุดที่ฉันสามารถทำได้คือการทำซ้ำตรรกะคอนโทรลเลอร์ทั้งหมดสำหรับHomeController.Index(..)ในGatewayController.IndexAction ของฉัน ซึ่งหมายความว่าฉันต้องย้าย'Views/Home'เข้าไป'Shared'จึงจะสามารถเข้าถึงได้ ต้องมีวิธีที่ดีกว่านี้ ?? ..


สิ่งServerTransferActionที่คุณพยายามทำซ้ำคืออะไร? นั่นคือสิ่งที่เกิดขึ้นจริงหรือ? (ไม่พบข้อมูลใด ๆ เลย ... ขอบคุณสำหรับคำถาม btw คำตอบด้านล่างสุดยอดมาก)
jleach

ค้นหาเซิร์ฟเวอร์โอน (... ) เป็นวิธีการ 'เปลี่ยนเส้นทาง' โดยทั่วไปบนฝั่งเซิร์ฟเวอร์ซึ่งไคลเอนต์ได้รับหน้าที่เปลี่ยนเส้นทางโดยไม่มีการเปลี่ยนเส้นทางฝั่งไคลเอ็นต์ โดยทั่วไปไม่แนะนำให้ใช้กับการกำหนดเส้นทางที่ทันสมัย
Simon_Weaver

1
"การถ่ายโอน" เป็นคุณลักษณะ ASP.NET รุ่นเก่าที่ไม่จำเป็นใน MVC อีกต่อไปเนื่องจากความสามารถในการดำเนินการโดยตรงกับตัวควบคุมที่ถูกต้องโดยใช้การกำหนดเส้นทาง ดูรายละเอียดคำตอบนี้
NightOwl888

@ NightOwl888 ใช่แน่นอน - แต่บางครั้งเนื่องจากตรรกะทางธุรกิจจำเป็น / ง่ายกว่า ฉันมองย้อนกลับไปเพื่อดูว่าฉันลงเอยด้วยการใช้สิ่งนี้ - (โชคดีที่มันอยู่ในที่เดียวเท่านั้น) - ที่ซึ่งฉันมีหน้าแรกที่ฉันต้องการให้เป็นแบบไดนามิกสำหรับเงื่อนไขที่ซับซ้อนบางอย่างดังนั้นเบื้องหลังจึงแสดงเส้นทางที่แตกต่างออกไป ต้องการหลีกเลี่ยงให้มากที่สุดเท่าที่จะเป็นไปได้ในการกำหนดเส้นทางหรือสภาพเส้นทาง - แต่บางครั้งifคำพูดง่ายๆก็เป็นวิธีแก้ปัญหาที่น่าดึงดูดเกินไป
Simon_Weaver

@Simon_Weaver - และมีอะไรผิดปกติกับคลาสย่อยRouteBaseเพื่อให้คุณสามารถวางifคำสั่งของคุณไว้ที่นั่นแทนที่จะงอทุกอย่างไปข้างหลังเพื่อข้ามจากคอนโทรลเลอร์หนึ่งไปยังอีกตัวหนึ่ง?
NightOwl888

คำตอบ:


130

แล้วคลาส TransferResult ล่ะ? (ขึ้นอยู่กับคำตอบของ Stans )

/// <summary>
/// Transfers execution to the supplied url.
/// </summary>
public class TransferResult : ActionResult
{
    public string Url { get; private set; }

    public TransferResult(string url)
    {
        this.Url = url;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var httpContext = HttpContext.Current;

        // MVC 3 running on IIS 7+
        if (HttpRuntime.UsingIntegratedPipeline)
        {
            httpContext.Server.TransferRequest(this.Url, true);
        }
        else
        {
            // Pre MVC 3
            httpContext.RewritePath(this.Url, false);

            IHttpHandler httpHandler = new MvcHttpHandler();
            httpHandler.ProcessRequest(httpContext);
        }
    }
}

อัปเดต:ตอนนี้ใช้งานได้กับ MVC3 (โดยใช้รหัสจากโพสต์ของ Simon ) มันควร (ยังไม่สามารถทดสอบได้) ทำงานใน MVC2 ด้วยโดยดูว่ามันทำงานภายในท่อรวมของ IIS7 + หรือไม่

เพื่อความโปร่งใสอย่างเต็มที่ ในสภาพแวดล้อมการผลิตของเราเราไม่เคยใช้ TransferResult โดยตรง เราใช้ TransferToRouteResult ซึ่งจะเรียกใช้ TransferResult นี่คือสิ่งที่ทำงานบนเซิร์ฟเวอร์ที่ใช้งานจริงของฉัน

public class TransferToRouteResult : ActionResult
{
    public string RouteName { get;set; }
    public RouteValueDictionary RouteValues { get; set; }

    public TransferToRouteResult(RouteValueDictionary routeValues)
        : this(null, routeValues)
    {
    }

    public TransferToRouteResult(string routeName, RouteValueDictionary routeValues)
    {
        this.RouteName = routeName ?? string.Empty;
        this.RouteValues = routeValues ?? new RouteValueDictionary();
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var urlHelper = new UrlHelper(context.RequestContext);
        var url = urlHelper.RouteUrl(this.RouteName, this.RouteValues);

        var actualResult = new TransferResult(url);
        actualResult.ExecuteResult(context);
    }
}

และถ้าคุณใช้T4MVC (ถ้าไม่ ... ทำ!) ส่วนขยายนี้อาจมีประโยชน์

public static class ControllerExtensions
{
    public static TransferToRouteResult TransferToAction(this Controller controller, ActionResult result)
    {
        return new TransferToRouteResult(result.GetRouteValueDictionary());
    }
}

ใช้อัญมณีเล็ก ๆ นี้คุณสามารถทำได้

// in an action method
TransferToAction(MVC.Error.Index());

1
มันใช้งานได้ดี ระวังอย่าให้การวนซ้ำไม่สิ้นสุดเช่นเดียวกับที่ฉันพยายามครั้งแรกโดยการส่ง URL ที่ไม่ถูกต้องฉันได้ทำการปรับเปลี่ยนเล็กน้อยเพื่อให้สามารถส่งผ่านคอลเล็กชันค่าเส้นทางซึ่งอาจเป็นประโยชน์กับผู้อื่น โพสต์ด้านบนหรือด้านล่าง ...
Simon_Weaver

อัปเดต: โซลูชันนี้ดูเหมือนจะทำงานได้ดีและแม้ว่าฉันจะใช้มันในความจุที่ จำกัด มาก แต่ก็ยังไม่พบปัญหาใด ๆ
Simon_Weaver

1
คำเตือน: Server.TransferRequest ไม่ทำงานกับ TempData Application_Error ถูกโจมตีแม้ว่าในรันไทม์คุณจะไม่เห็นสิ่งผิดปกติ (ยกเว้น TempData จะว่างเปล่า) ดูคำตอบของฉันสำหรับรายละเอียด ขออภัยรายการคำตอบเหล่านี้ค่อนข้างซับซ้อน!
Simon_Weaver

2
@BradLaney: คุณสามารถลบบรรทัด 'var urlHelper ... ' และ 'var url ... ' และแทนที่ 'url' ด้วย 'this.Url' สำหรับส่วนที่เหลือและใช้งานได้ :)
Michael Ulmann

1
1: การมีเพศสัมพันธ์ / การทดสอบหน่วย / ความเข้ากันได้ในอนาคต 2: mvc core / mvc ตัวอย่างไม่เคยใช้ singleton นี้ 3: singleton นี้ไม่พร้อมใช้งานในเธรด (null) ไม่ว่าจะเป็นเธรดพูลหรือตัวแทน async ที่เรียกบนบริบทอื่นที่ไม่ใช่ค่าดีฟอลต์เช่นเมื่อใช้วิธีการดำเนินการ async 4: เพื่อความเข้ากันได้เท่านั้น mvc ตั้งค่า singleton นี้เป็นบริบท httpContext ก่อนป้อนรหัสผู้ใช้
Softlion

48

แก้ไข: ปรับปรุงเพื่อให้เข้ากันได้กับ ASP.NET MVC 3

หากคุณใช้ IIS7 การปรับเปลี่ยนต่อไปนี้ดูเหมือนว่าจะใช้ได้กับ ASP.NET MVC 3 ขอบคุณ @nitin และ @andy ที่ชี้ให้เห็นว่าโค้ดดั้งเดิมไม่ทำงาน

แก้ไข 4/11/2011: TempData หยุดทำงานกับ Server.TransferRequest ณ MVC 3 RTM

แก้ไขโค้ดด้านล่างเพื่อให้เกิดข้อยกเว้น - แต่ยังไม่มีวิธีแก้ปัญหาอื่นในขณะนี้


นี่คือการปรับเปลี่ยนของฉันตามโพสต์ต้นฉบับของ Stan ที่ดัดแปลงโดย Markus ฉันเพิ่มตัวสร้างเพิ่มเติมเพื่อใช้พจนานุกรมค่าเส้นทาง - และเปลี่ยนชื่อเป็น MVCTransferResult เพื่อหลีกเลี่ยงความสับสนว่าอาจเป็นการเปลี่ยนเส้นทาง

ตอนนี้ฉันสามารถดำเนินการต่อไปนี้สำหรับการเปลี่ยนเส้นทาง:

return new MVCTransferResult(new {controller = "home", action = "something" });

ชั้นเรียนที่แก้ไขของฉัน:

public class MVCTransferResult : RedirectResult
{
    public MVCTransferResult(string url)
        : base(url)
    {
    }

    public MVCTransferResult(object routeValues):base(GetRouteURL(routeValues))
    {
    }

    private static string GetRouteURL(object routeValues)
    {
        UrlHelper url = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()), RouteTable.Routes);
        return url.RouteUrl(routeValues);
    }

    public override void ExecuteResult(ControllerContext context)
    {
        var httpContext = HttpContext.Current;

        // ASP.NET MVC 3.0
        if (context.Controller.TempData != null && 
            context.Controller.TempData.Count() > 0)
        {
            throw new ApplicationException("TempData won't work with Server.TransferRequest!");
        }

        httpContext.Server.TransferRequest(Url, true); // change to false to pass query string parameters if you have already processed them

        // ASP.NET MVC 2.0
        //httpContext.RewritePath(Url, false);
        //IHttpHandler httpHandler = new MvcHttpHandler();
        //httpHandler.ProcessRequest(HttpContext.Current);
    }
}

1
ดูเหมือนว่าจะไม่ทำงานใน MVC 3 RC ล้มเหลวใน HttpHandler.ProcessRequest () กล่าวว่า: 'HttpContext.SetSessionStateBehavior' สามารถเรียกใช้ก่อนที่เหตุการณ์ 'HttpApplication.AcquireRequestState' จะถูกยกขึ้นเท่านั้น
Andy

ฉันยังไม่ได้เปลี่ยนไปดู MVC3 แจ้งให้เราทราบหากคุณพบวิธีแก้ปัญหา
Simon_Weaver

Server.TransferRquest ตามที่ Nitin แนะนำทำในสิ่งที่พยายามทำข้างต้นหรือไม่
Old Geezer

ทำไมเราต้องตรวจสอบ TempData ว่า null และ count> 0?
yurart

คุณทำไม่ได้ แต่เป็นเพียงคุณสมบัติด้านความปลอดภัยดังนั้นหากคุณใช้งานอยู่แล้วและพึ่งพามันคุณจะไม่ต้องเกาหัวถ้ามันหายไป
Simon_Weaver


12

เมื่อเร็ว ๆ นี้ฉันพบว่า ASP.NET MVC ไม่รองรับ Server.Transfer () ดังนั้นฉันจึงสร้างวิธีการต้นขั้ว (แรงบันดาลใจจาก Default.aspx.cs)

    private void Transfer(string url)
    {
        // Create URI builder
        var uriBuilder = new UriBuilder(Request.Url.Scheme, Request.Url.Host, Request.Url.Port, Request.ApplicationPath);
        // Add destination URI
        uriBuilder.Path += url;
        // Because UriBuilder escapes URI decode before passing as an argument
        string path = Server.UrlDecode(uriBuilder.Uri.PathAndQuery);
        // Rewrite path
        HttpContext.Current.RewritePath(path, false);
        IHttpHandler httpHandler = new MvcHttpHandler();
        // Process request
        httpHandler.ProcessRequest(HttpContext.Current);
    }

9

คุณไม่สามารถสร้างอินสแตนซ์ของคอนโทรลเลอร์ที่คุณต้องการเปลี่ยนเส้นทางเรียกใช้วิธีการดำเนินการที่คุณต้องการจากนั้นส่งคืนผลลัพธ์ของสิ่งนั้นได้หรือไม่ สิ่งที่ต้องการ:

 HomeController controller = new HomeController();
 return controller.Index();

4
ไม่คอนโทรลเลอร์ที่คุณสร้างจะไม่มีสิ่งต่างๆเช่นการตั้งค่าคำขอและการตอบกลับอย่างถูกต้อง ที่สามารถนำไปสู่ปัญหา
Jeff Walker Code Ranger

ฉันเห็นด้วยกับ @JeffWalkerCodeRanger: สิ่งเดียวกันหลังจากตั้งค่าคุณสมบัติotherController.ControllerContext = this.ControllerContext;
T-moty

7

ฉันต้องการกำหนดเส้นทางคำขอปัจจุบันไปยังคอนโทรลเลอร์ / การดำเนินการอื่นในขณะที่รักษาพา ธ การดำเนินการให้เหมือนกับว่ามีการร้องขอคอนโทรลเลอร์ / การดำเนินการที่สอง ในกรณีของฉัน Server.Request ไม่ทำงานเพราะฉันต้องการเพิ่มข้อมูล นี่เทียบเท่ากับตัวจัดการปัจจุบันที่เรียกใช้ HTTP GET / POST อื่นจากนั้นสตรีมผลลัพธ์ไปยังไคลเอนต์ ฉันแน่ใจว่าจะมีวิธีที่ดีกว่านี้ในการบรรลุเป้าหมายนี้ แต่นี่คือสิ่งที่เหมาะกับฉัน:

RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Public");
routeData.Values.Add("action", "ErrorInternal");
routeData.Values.Add("Exception", filterContext.Exception);

var context = new HttpContextWrapper(System.Web.HttpContext.Current);
var request = new RequestContext(context, routeData);

IController controller = ControllerBuilder.Current.GetControllerFactory().CreateController(filterContext.RequestContext, "Public");
controller.Execute(request);

คุณเดาถูก: ฉันใส่รหัสนี้ไว้

public class RedirectOnErrorAttribute : ActionFilterAttribute, IExceptionFilter

และฉันกำลังใช้มันเพื่อแสดงข้อผิดพลาดให้กับนักพัฒนาในขณะที่จะใช้การเปลี่ยนเส้นทางปกติในการผลิต โปรดทราบว่าฉันไม่ต้องการใช้เซสชัน ASP.NET ฐานข้อมูลหรือวิธีอื่น ๆ ในการส่งผ่านข้อมูลข้อยกเว้นระหว่างคำขอ


7

มากกว่าการถ่ายโอนเซิร์ฟเวอร์จำลอง, MVC ยังคงความสามารถในการทำจริงServer.TransferRequest :

public ActionResult Whatever()
{
    string url = //...
    Request.RequestContext.HttpContext.Server.TransferRequest(url);
    return Content("success");//Doesn't actually get returned
}

อย่าลังเลที่จะเพิ่มข้อความในคำตอบของคุณเพื่ออธิบายเพิ่มเติม
Wladimir Palant

โปรดทราบว่าสิ่งนี้ต้องใช้ MVCv3 ขึ้นไป
ก.ย.

5

เพียงแค่อินสแตนซ์คอนโทรลเลอร์อื่น ๆ และดำเนินการตามวิธีการดำเนินการ


สิ่งนี้จะไม่แสดง URL ที่ต้องการในแถบที่อยู่
arserbin3

@ arserbin3 - เซิร์ฟเวอร์จะไม่โอน ข้อกำหนดนี้น่าจะเป็นสาเหตุที่ทำให้มีการโพสต์คำถามเดิมด้วยซ้ำ
Richard Szalay

2

คุณสามารถสร้างตัวควบคุมอื่นขึ้นมาใหม่และเรียกใช้วิธีการดำเนินการที่ส่งคืนผลลัพธ์ อย่างไรก็ตามสิ่งนี้จะทำให้คุณต้องวางมุมมองของคุณลงในโฟลเดอร์ที่แชร์

ฉันไม่แน่ใจว่านี่คือสิ่งที่คุณหมายถึงการทำซ้ำ แต่:

return new HomeController().Index();

แก้ไข

อีกทางเลือกหนึ่งคือการสร้าง ControllerFactory ของคุณเองด้วยวิธีนี้คุณสามารถกำหนดคอนโทรลเลอร์ที่จะสร้างได้


นี่อาจเป็นแนวทาง แต่ดูเหมือนจะไม่ค่อยมีบริบทที่ถูกต้อง - แม้ว่าฉันจะพูดว่า hc.ControllerContext = this.ControllerContext จากนั้นค้นหามุมมองภายใต้ ~ / Views / Gateway / 5.aspx แล้วไม่พบ
Simon_Weaver

นอกจากนี้คุณยังสูญเสียตัวกรองการดำเนินการทั้งหมด คุณอาจต้องการลองใช้เมธอด Execute บนอินเทอร์เฟซ IController ที่คอนโทรลเลอร์ของคุณต้องใช้ ตัวอย่างเช่น: ((IController) HomeController ใหม่ ()) ดำเนินการ (... ) ด้วยวิธีนี้คุณยังคงมีส่วนร่วมในไปป์ไลน์ Action Invoker คุณต้องคิดให้ออกว่าจะส่งผ่านอะไรไปยัง Execute แม้ว่า ... ตัวสะท้อนแสงอาจช่วยได้ :)
Andrew Stanton-Nurse

ใช่ฉันไม่ชอบแนวคิดในการสร้างคอนโทรลเลอร์ใหม่ฉันคิดว่าคุณควรกำหนดโรงงานควบคุมของคุณเองซึ่งดูเหมือนจะเป็นจุดขยายที่เหมาะสมสำหรับสิ่งนี้ แต่ฉันแทบไม่มีรอยขีดข่วนที่พื้นผิวของเฟรมเวิร์กนี้ดังนั้นฉันจึงอาจหลีกหนีได้
JoshBerke

1

การกำหนดเส้นทางไม่ได้ดูแลสถานการณ์นี้ให้คุณหรือ กล่าวคือสำหรับสถานการณ์ที่อธิบายไว้ข้างต้นคุณสามารถสร้างตัวจัดการเส้นทางที่ใช้ตรรกะนี้ได้


ขึ้นอยู่กับเงื่อนไขทางโปรแกรม เช่นแคมเปญ 100 อาจไปที่มุมมอง 7 และแคมเปญ 200 อาจไปที่มุมมอง 8 ฯลฯ ฯลฯ ซับซ้อนเกินไปสำหรับการกำหนดเส้นทาง
Simon_Weaver

4
เหตุใดจึงซับซ้อนเกินไปสำหรับการกำหนดเส้นทาง เกิดอะไรขึ้นกับข้อ จำกัด เส้นทางที่กำหนดเอง stephenwalther.com/blog/archive/2008/08/07/…
Ian Mercer

1

สำหรับใครก็ตามที่ใช้การกำหนดเส้นทางตามนิพจน์โดยใช้เฉพาะคลาส TransferResult ด้านบนต่อไปนี้เป็นวิธีการขยายคอนโทรลเลอร์ที่ใช้กลอุบายและรักษา TempData ไม่จำเป็นต้อง TransferToRouteResult

public static ActionResult TransferRequest<T>(this Controller controller, Expression<Action<T>> action)
    where T : Controller
{
     controller.TempData.Keep();
     controller.TempData.Save(controller.ControllerContext, controller.TempDataProvider);
     var url = LinkBuilder.BuildUrlFromExpression(controller.Request.RequestContext, RouteTable.Routes, action);
     return new TransferResult(url);
}

1
คำเตือน: ดูเหมือนว่าจะทำให้เกิดข้อผิดพลาด 'คลาส SessionStateTempDataProvider ต้องเปิดใช้งานสถานะเซสชัน' แม้ว่าจะยังใช้งานได้จริงก็ตาม ฉันเห็นข้อผิดพลาดนี้ในบันทึกของฉันเท่านั้น ฉันใช้ ELMAH เพื่อบันทึกข้อผิดพลาดและรับข้อผิดพลาดนี้สำหรับ InProc และ AppFabric
Simon_Weaver

1

Server.TransferRequestเป็นสมบูรณ์ที่ไม่จำเป็นใน MVC นี่เป็นคุณลักษณะเก่าแก่ที่จำเป็นใน ASP.NET เท่านั้นเนื่องจากคำขอมาที่เพจโดยตรงและจำเป็นต้องมีวิธีการโอนคำขอไปยังเพจอื่น รุ่นที่ทันสมัยของ ASP.NET (รวม MVC) มีโครงสร้างพื้นฐานการกำหนดเส้นทางที่สามารถปรับแต่งเพื่อให้เส้นทางโดยตรงกับทรัพยากรที่เป็นที่ต้องการ ไม่มีจุดใดที่จะให้คำขอไปถึงคอนโทรลเลอร์เพียงเพื่อถ่ายโอนไปยังคอนโทรลเลอร์อื่นเมื่อคุณสามารถส่งคำขอไปยังคอนโทรลเลอร์และการดำเนินการที่คุณต้องการได้โดยตรง

ยิ่งไปกว่านั้นเนื่องจากคุณตอบสนองต่อคำขอเดิมคุณจึงไม่จำเป็นต้องเก็บอะไรไว้ในTempDataหรือที่เก็บข้อมูลอื่นเพียงเพื่อกำหนดเส้นทางคำขอไปยังที่ที่ถูกต้อง แต่คุณมาถึงการดำเนินการของคอนโทรลเลอร์พร้อมกับคำขอเดิมที่ไม่เสียหาย นอกจากนี้คุณยังสามารถมั่นใจได้ว่า Google จะอนุมัติแนวทางนี้เนื่องจากเกิดขึ้นในฝั่งเซิร์ฟเวอร์ทั้งหมด

แม้ว่าคุณจะทำได้ไม่น้อยจากทั้งสองIRouteConstraintและIRouteHandlerจุดขยายที่มีประสิทธิภาพที่สุดสำหรับการกำหนดเส้นทางคือRouteBaseคลาสย่อย คลาสนี้สามารถขยายเพื่อให้ทั้งเส้นทางขาเข้าและการสร้าง URL ขาออกซึ่งทำให้เป็นร้านค้าครบวงจรสำหรับทุกสิ่งที่เกี่ยวข้องกับ URL และการดำเนินการที่ URL ดำเนินการ

ดังนั้นในการทำตามตัวอย่างที่สองในการเดินทางจาก/ไป/home/7คุณเพียงแค่ต้องมีเส้นทางที่เพิ่มค่าเส้นทางที่เหมาะสม

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

        // Routes directy to `/home/7`
        routes.MapRoute(
            name: "Home7",
            url: "",
            defaults: new { controller = "Home", action = "Index", version = 7 }
        );

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

แต่กลับไปที่ตัวอย่างเดิมของคุณที่คุณมีเพจแบบสุ่มมันซับซ้อนกว่าเนื่องจากพารามิเตอร์เส้นทางไม่สามารถเปลี่ยนแปลงได้เมื่อรันไทม์ ดังนั้นจึงสามารถทำได้ด้วยRouteBaseคลาสย่อยดังนี้

public class RandomHomePageRoute : RouteBase
{
    private Random random = new Random();

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

        // Only handle the home page route
        if (httpContext.Request.Path == "/")
        {
            result = new RouteData(this, new MvcRouteHandler());

            result.Values["controller"] = "Home";
            result.Values["action"] = "Index";
            result.Values["version"] = random.Next(10) + 1; // Picks a random number from 1 to 10
        }

        // If this isn't the home page route, this should return null
        // which instructs routing to try the next route in the route table.
        return result;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        var controller = Convert.ToString(values["controller"]);
        var action = Convert.ToString(values["action"]);

        if (controller.Equals("Home", StringComparison.OrdinalIgnoreCase) &&
            action.Equals("Index", StringComparison.OrdinalIgnoreCase))
        {
            // Route to the Home page URL
            return new VirtualPathData(this, "");
        }

        return null;
    }
}

ซึ่งสามารถลงทะเบียนในการกำหนดเส้นทางเช่น:

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

        // Routes to /home/{version} where version is randomly from 1-10
        routes.Add(new RandomHomePageRoute());

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

โปรดทราบว่าในตัวอย่างข้างต้นอาจเป็นเรื่องที่สมเหตุสมผลที่จะจัดเก็บคุกกี้ที่บันทึกเวอร์ชันโฮมเพจที่ผู้ใช้เข้ามาดังนั้นเมื่อพวกเขากลับมาพวกเขาจะได้รับโฮมเพจเวอร์ชันเดียวกัน

โปรดทราบว่าการใช้แนวทางนี้คุณสามารถปรับแต่งการกำหนดเส้นทางเพื่อนำพารามิเตอร์สตริงการสืบค้นมาพิจารณา (โดยจะละเว้นโดยค่าเริ่มต้นโดยสิ้นเชิง) และกำหนดเส้นทางไปยังการดำเนินการของคอนโทรลเลอร์ที่เหมาะสมตามนั้น

ตัวอย่างเพิ่มเติม


จะเกิดอะไรขึ้นถ้าฉันไม่ต้องการโอนทันทีเมื่อเข้าสู่การดำเนินการ แต่ปล่อยให้การกระทำนั้นทำงานบางอย่างแล้วโอนไปยังการกระทำอื่นตามเงื่อนไข การเปลี่ยนเส้นทางของฉันเพื่อไปยังเป้าหมายการโอนโดยตรงจะไม่ได้ผลดังนั้นดูเหมือนว่าServer.TransferRequestจะไม่ "ไม่จำเป็นอย่างสมบูรณ์ใน MVC"
ProfK

0

ไม่ใช่คำตอบสำหรับแต่ละข้อ แต่เห็นได้ชัดว่าข้อกำหนดจะไม่เพียง แต่สำหรับการนำทางจริงในการ "ทำ" ฟังก์ชันการทำงานที่เทียบเท่าของ Webforms Server.Transfer () แต่ยังรวมถึงการสนับสนุนทั้งหมดนี้ในการทดสอบหน่วยด้วย

ดังนั้น ServerTransferResult ควร "ดูเหมือน" RedirectToRouteResult และมีความคล้ายคลึงกันมากที่สุดในแง่ของลำดับชั้นของคลาส

ฉันกำลังคิดที่จะทำสิ่งนี้โดยดูที่ตัวสะท้อนแสงและทำอะไรก็ตามที่คลาส RedirectToRouteResult และวิธีการคลาสพื้นฐานของคอนโทรลเลอร์ต่างๆทำจากนั้นจึง "เพิ่ม" ตัวหลังให้กับคอนโทรลเลอร์ผ่านวิธีการขยาย บางทีสิ่งเหล่านี้อาจเป็นวิธีการคงที่ภายในคลาสเดียวกันเพื่อความสะดวก / ความเกียจคร้านในการดาวน์โหลด?

ถ้าฉันทำสิ่งนี้ได้ฉันจะโพสต์มันไว้ไม่งั้นคนอื่นอาจเอาชนะฉันได้!


0

ฉันทำได้โดยการควบคุมHtml.RenderActionผู้ช่วยในมุมมอง:

@{
    string action = ViewBag.ActionName;
    string controller = ViewBag.ControllerName;
    object routeValues = ViewBag.RouteValues;
    Html.RenderAction(action, controller, routeValues);
}

และในตัวควบคุมของฉัน:

public ActionResult MyAction(....)
{
    var routeValues = HttpContext.Request.RequestContext.RouteData.Values;    
    ViewBag.ActionName = "myaction";
    ViewBag.ControllerName = "mycontroller";
    ViewBag.RouteValues = routeValues;    
    return PartialView("_AjaxRedirect");
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.