3

Google アナリティクス データをクエリして取得した URL のリストがあります。これらの各 URL を MVC パイプラインで実行して、ActionResult を取得したいと考えています。アクションの結果には、いくつかの重要な情報を抽出できるビュー モデルが含まれています。

MVC の拡張性に基づいて、これは簡単だと思いました。文字列 URL を使用して HttpRequest をモックアップし、ルーティングとコントローラーを介して渡すことができると考えました。私のエンドポイントは、ActionResult を返すアクション メソッドを呼び出すことです。必要なものの断片を見つけていますが、多くのメソッドはさまざまなクラス内で保護されており、それらに関するドキュメントはかなりまばらです。

どうにかして ControllerActionInvoker に到達し、保護された関数 InvokeActionMethod への呼び出しの結果を取得したいと考えています。

4

4 に答える 4

3

まず第一に、Darin の回答から始めましたが、最終的な解決策にはさらに多くの詳細があるため、別の回答を追加しています。これは複雑なので、我慢してください。

URL から ViewResult を取得するには、次の 4 つの手順があります。

  1. ルーティング システムを介して RequestContext をモックします (Darin の回答により、これを開始できました)。

            Uri uri = new Uri(MyStringUrl);
            var request = new HttpRequest(null, uri.Scheme + "://" + uri.Authority + uri.AbsolutePath, string.IsNullOrWhiteSpace(uri.Query) ? null : uri.Query.Substring(1));
            var response = new HttpResponse(new StringWriter());
            var context = new HttpContext(request, response);
            var contextBase = new HttpContextWrapper(context);
            var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(contextBase);
    
            // We shouldn't have to do this, but the way we are mocking the request doesn't seem to pass the querystring data through to the route data.
            foreach (string key in request.QueryString.Keys)
            {
                if (!routeData.Values.ContainsKey(key))
                {
                    routeData.Values.Add(key, request.QueryString[key]);
                }
            }
    
            var requestContext = new System.Web.Routing.RequestContext(contextBase, routeData);
    
  2. コントローラーをサブクラス化します。保護された Execute(RequestContext) メソッドを呼び出すことができるパブリック メソッドを追加します。

    public void MyExecute(System.Web.Routing.RequestContext requestContext)
    {
        this.Execute(requestContext);
    }
    
  3. 同じサブクラス化されたコントローラーで、保護された OnActionExecuted イベントにフックするパブリック イベントを追加します。これにより、ActionExecutedContext を介して ViewResult を取得できます。

    public delegate void MyActionExecutedHandler(ActionExecutedContext filterContext);
    
    public event MyActionExecutedHandler MyActionExecuted;
    
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        if (MyActionExecuted != null)
        {
            MyActionExecuted(filterContext);
        }
    }
    
  4. 新しいコントローラー サブクラスのインスタンスをインスタンス化し、イベント ハンドラーを追加し、新しい public execute メソッドを呼び出す (モック化された RequestContext を渡す) ことによって、すべてを結び付けます。イベント ハンドラーは、ViewResult へのアクセスを提供します。

            using (MyCompany.Controllers.MyController c = new Controllers.MyController())
            {
                c.MyActionExecuted += GrabActionResult;
                try
                {
                    c.MyExecute(requestContext);
                }
                catch (Exception)
                {
                    // Handle an exception.
                }
            }
    

イベントハンドラは次のとおりです。

        private void GrabActionResult(System.Web.Mvc.ActionExecutedContext context)
        {
            if (context.Result.GetType() == typeof(ViewResult))
            {
                ViewResult result = context.Result as ViewResult;
            }
            else if (context.Result.GetType() == typeof(RedirectToRouteResult))
            {
                // Handle.
            }
            else if (context.Result.GetType() == typeof(HttpNotFoundResult))
            {
                // Handle.
            }
            else
            {
                // Handle.
            }
        }
于 2013-06-25T16:13:12.280 に答える
1

ここでの難しさは、URL を構成要素のコントローラーとアクションに解析することです。これを行う方法は次のとおりです。

var url = "http://example.com/Home/Index";
var request = new HttpRequest(null, url, "");
var response = new HttpResponse(new StringWriter.Null);
var context = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
var values = routeData.Values;
var controller = values["controller"];
var action = values["action"];

コントローラーとアクションがわかったので、リフレクションを使用してインスタンス化して実行できます。

于 2012-12-20T22:13:18.597 に答える
0

これを試して:

object result = null;
Type controller = Type.GetType("MvcApplication4.Controllers.HomeController");
if (controller != null)
{
    object controllerObj = Activator.CreateInstance(controller, null);
    if (controller.GetMethod("ActionName") != null)
    {
        result = ((ViewResult)controller.GetMethod("ActionName").Invoke(controllerObj, null)).ViewData.Model;
    }
}

通常のルートはアプリケーションで構成されており、正規表現または文字列操作を使用して取得できると想定しました。あなたの議論に続いて、リフレクションやハードコーディング手法を使用せずにフレームワークを掘り下げて、MVC パイプラインを実際に追跡したいということを知りました。ただし、このスレッドに従って、アプリケーションで構成されたルートと URL を一致させようとすることで、ハードコーディングを最小限に抑えるために検索を試みました

任意の URL が定義済みのルートと一致するかどうかを判断する方法

また、routedata オブジェクトにアクセスするために httprequest を作成する他のスレッドに出くわしましたが、これにはリフレクションを使用する必要があります。

RouteValueDictionary への文字列 URL

于 2012-12-20T21:36:31.007 に答える
0

Ben Millsに感謝します。これにより、私自身の問題が始まりました。ただし、次のことを行うことで、2、3、または 4 を実行する必要がないことがわかりました。

Uri uri = new Uri(MyStringUrl);
var absoluteUri = uri.Scheme + "://" + uri.Authority + uri.AbsolutePath;
var query = string.IsNullOrWhiteSpace(uri.Query) ? null : uri.Query.Substring(1);
var request = new HttpRequest(null, absoluteUri, query);

文字列ライターにアクセスすることは重要です。

var sw = new StringWriter();
var response = new HttpResponse(sw);
var context = new HttpContext(request, response);
var contextBase = new HttpContextWrapper(context);
var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(contextBase);

RouteData をリクエスト コンテキストに割り当てると、意図したとおりに MVC パイプラインを使用できます。

request.RequestContext.RouteData = routeData;

var controllerName = routeData.GetRequiredString("controller");

var factory = ControllerBuilder.Current.GetControllerFactory();
var contoller = factory.CreateController(request.RequestContext, controllerName);

controller.Execute(request.RequestContext);

var viewResult = sw.ToString(); // this is our view result.

factory.ReleaseController(controller);
sw.Dispose();

これが、同様のことを達成したい他の誰かに役立つことを願っています。

于 2014-07-29T08:15:27.020 に答える