124

ASP.NET MVCでは、リダイレクトActionResultを非常に簡単に返すことができます。

 return RedirectToAction("Index");

 or

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

これにより、実際にはHTTPリダイレクトが提供されますが、通常は問題ありません。ただし、Google Analyticsを使用する場合、元のリファラーが失われるため、これは大きな問題を引き起こします。そのため、Googleはあなたがどこから来たのかわかりません。これにより、検索エンジンの用語などの有用な情報が失われます。

ちなみに、この方法には、キャンペーンから取得された可能性のあるパラメータをすべて削除できるという利点がありますが、それでもサーバー側でそれらをキャプチャできます。それらをクエリ文字列に残すと、ブックマークしたり、Twitterやブログに投稿したりしてはいけないリンクが表示されます。キャンペーンIDを含む私たちのサイトへのリンクを人々がツイッターで送っているところを何度か見ました。

とにかく、私は別の場所や別のバージョンにリダイレクトする可能性のあるサイトへのすべての着信訪問用の「ゲートウェイ」コントローラーを作成しています。

今のところ私は(偶然のブックマークよりも)Googleに関心があり、ホームページのバージョン7であるページにアクセス/したときに表示されるページにアクセスした人を送信できるようにしたいと考えています。/home/7

前に言ったように、これを行うと、グーグルがリファラーを分析する機能が失われます。

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

私が本当に欲しいのは

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

これにより、クライアント側のリダイレクトなしでそのビューを取得できます。でも、そんなことはないと思います。

現在、私が思いつくことができる最善のことはHomeController.Index(..)GatewayController.Indexアクションでコントローラーロジック全体を複製することです。これは、アクセスできるように移動'Views/Home'する'Shared'必要があったことを意味します。より良い方法があるに違いありません。

4

14 に答える 14

131

TransferResultクラスはどうですか?(スタンズの回答に基づく)

/// <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の投稿のコードを使用)。IIS7 +の統合パイプライン内で実行されているかどうかを確認することで、MVC2でも機能するはずです(テストできていません)。

完全な透明性のために; 実稼働環境では、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());
于 2009-07-25T00:03:38.447 に答える
48

編集:ASP.NETMVC3と互換性があるように更新されました

IIS7を使用している場合、ASP.NETMVC3では次の変更が機能するようです。元のコードが機能しなかったことを指摘してくれた@nitinと@andyに感謝します。

2011年4月11日編集:MVC3RTMの時点でTempDataがServer.TransferRequestで壊れています

以下のコードを変更して例外をスローしましたが、現時点では他の解決策はありません。


これは、MarkusによるStanの元の投稿の変更バージョンに基づく私の変更です。ルート値ディクショナリを取得するコンストラクターを追加し、リダイレクトである可能性があるという混乱を避けるために、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);
    }
}
于 2009-08-07T01:57:33.317 に答える
15

代わりに、IIS7+でServer.TransferRequestを使用できます。

于 2010-09-17T21:09:29.597 に答える
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);
    }
于 2009-05-21T11:50:20.143 に答える
9

リダイレクト先のコントローラーのインスタンスを作成し、目的のアクションメソッドを呼び出して、その結果を返すだけではいけませんか?何かのようなもの:

 HomeController controller = new HomeController();
 return controller.Index();
于 2009-04-28T19:42:17.317 に答える
9

MVCは、サーバー転送をシミュレートするのではなく、実際にServer.TransferRequestを実行できます。

public ActionResult Whatever()
{
    string url = //...
    Request.RequestContext.HttpContext.Server.TransferRequest(url);
    return Content("success");//Doesn't actually get returned
}
于 2014-01-03T21:49:04.100 に答える
7

2番目のコントローラー/アクションが要求された場合とまったく同じ実行パスを維持しながら、現在の要求を別のコントローラー/アクションに再ルーティングしたかったのです。私の場合、データを追加したかったので、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セッション、データベース、またはその他の方法を使用して、要求間で例外データを渡したくなかったことに注意してください。

于 2009-10-18T10:43:02.207 に答える
5

他のコントローラーをインスタンス化して、そのアクションメソッドを実行するだけです。

于 2009-04-28T19:42:23.523 に答える
2

他のコントローラーを新しくして、結果を返すアクションメソッドを呼び出すことができます。ただし、これには、ビューを共有フォルダーに配置する必要があります。

これが重複の意味であるかどうかはわかりませんが、次のようになります。

return new HomeController().Index();

編集

もう1つのオプションは、独自のControllerFactoryを作成することです。これにより、作成するコントローラーを決定できます。

于 2009-04-28T19:47:48.287 に答える
2

Server.TransferRequestMVCでは完全に不要です。これは、要求がページに直接送信され、要求を別のページに転送する方法が必要だったため、ASP.NETでのみ必要だった時代遅れの機能です。最新バージョンのASP.NET(MVCを含む)には、目的のリソースに直接ルーティングするようにカスタマイズできるルーティングインフラストラクチャがあります。要求をコントローラーに直接送信して必要なアクションを実行できる場合は、要求をコントローラーに到達させて別のコントローラーに転送するだけでは意味がありません。

さらに、元のリクエストに応答しているTempDataため、リクエストを適切な場所にルーティングするためだけに、ストレージやその他のストレージに何かを入れる必要はありません。代わりに、元の要求をそのままにしてコントローラーアクションに到達します。また、このアプローチは完全にサーバー側で行われるため、Googleがこのアプローチを承認するので安心できます。

IRouteConstraintとの両方からかなりのことができますがIRouteHandler、ルーティングの最も強力な拡張ポイントはRouteBaseサブクラスです。このクラスは、着信ルートと発信URL生成の両方を提供するように拡張できます。これにより、URLとURLが実行するアクションに関係するすべてのことをワンストップショップで行うことができます。

したがって、2番目の例に従って、からに/移動する/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 }
        );
    }
}

上記の例では、ユーザーがアクセスしたホームページのバージョンを記録したCookieも保存して、ユーザーが戻ったときに同じホームページのバージョンを受け取るようにすることも意味があることに注意してください。

このアプローチを使用すると、ルーティングをカスタマイズしてクエリ文字列パラメーターを考慮に入れ(デフォルトでは完全に無視されます)、それに応じて適切なコントローラーアクションにルーティングできることにも注意してください。

その他の例

于 2018-01-20T14:59:28.613 に答える
1

ルーティングは、このシナリオを処理するだけではありませんか?つまり、上記のシナリオでは、このロジックを実装するルートハンドラーを作成するだけで済みます。

于 2009-04-28T19:40:20.473 に答える
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);
}
于 2012-10-18T17:43:02.637 に答える
0

それ自体は答えではありませんが、実際のナビゲーションでWebforms Server.Transfer()と同等の機能を「実行」するだけでなく、これらすべてを単体テストで完全にサポートする必要があることは明らかです。

したがって、ServerTransferResultはRedirectToRouteResultのように「見え」、クラス階層に関して可能な限り類似している必要があります。

これを行うには、Reflectorを確認し、RedirectToRouteResultクラスとさまざまなController基本クラスメソッドを実行してから、拡張メソッドを介して後者をControllerに「追加」することを考えています。ダウンロードを簡単/怠惰にするために、これらは同じクラス内の静的メソッドである可能性がありますか?

私がこれを行うことに慣れたら、私はそれを投稿します、さもなければ誰かが私をそれに打ち負かすかもしれません!

于 2009-12-29T17:15:58.143 に答える
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");
}
于 2018-03-06T17:56:43.177 に答える