1. ホーム
  2. asp.net-mvc

[解決済み] ASP.NET MVCでServer.Transferをシミュレートする方法とは?

2022-08-25 07:23:20

質問

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

 return RedirectToAction("Index");

 or

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

これは実際にHTTPリダイレクトを与えるので、通常は問題ありません。しかし、Google Analyticsを使用する場合、元のリファラーが失われるため、Googleはあなたがどこから来たのか分からないという大きな問題が発生します。このため、検索エンジンの用語などの有用な情報が失われます。

余談ですが、この方法には、キャンペーンから来たかもしれないパラメータを削除しても、サーバー側でそれらを捕捉できるという利点があります。クエリ文字列にパラメータを残すと、ブックマークしたり、Twitter やブログにリンクを貼ったりする人が出てきますが、これはいけないことです。私は、キャンペーン ID を含む私たちのサイトへのリンクを Twitter で投稿した人々を何度か見てきました。

とにかく、私は、異なる場所または代替バージョンにリダイレクトする可能性があるサイトへのすべての着信のための「ゲートウェイ」コントローラを書いています。

今のところ、私は(偶然のブックマークよりも)Googleについてもっと気にしており、私は、サイトにアクセスした人を / にアクセスした場合に表示されるページに送りたいのです。 /home/7 にアクセスした場合に表示されるページ、つまりホームページのバージョン7になります。

このようにすると、googleがリファラーを分析する能力を失うと言いました。

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

私が本当に欲しいのは

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

で、クライアントサイドのリダイレクトなしでそのビューを取得できます。 でも、そんなものは存在しないと思います。

現在、私が思いつく最善の方法は、コントローラロジック全体を HomeController.Index(..) を私の GatewayController.Index アクションを行います。つまり、私は 'Views/Home' の中に 'Shared' に変換することで、アクセス可能になりました。 もっといい方法があるはずです。

どのように解決するのですか?

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());
    }
}

この小さなgemを使うと、次のようなことができます。

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