2012-12-20 54 views
3

我有一個通過查詢Google Analytics數據獲得的URL列表。我想通過MVC管道運行這些URL中的每一個來獲取ActionResult。操作結果包含可從中提取一些重要信息的視圖模型。通過ASP.NET MVC管道運行URL字符串以獲取ActionResult

基於MVC的可擴展性,我認爲這很容易。我想我可以使用字符串URL來模擬HttpRequest,並通過路由和控制器傳遞它。我的終點將調用將返回ActionResult的操作方法。我找到了我需要的東西,但很多方法在各個類中都受到保護,而且它們上的文檔相當稀疏。

我以某種方式想要到達ControllerActionInvoker並獲得對受保護函數InvokeActionMethod的調用結果。

+0

你好嗎*運行*這些網址? –

+0

我只是說我的輸入是一個字符串URL,我想要傳入MVC管道,並在調用操作方法後提取ActionResult。這些URL在您的MVC應用程序中是 –

+0

。我的意思是你試圖在同一個應用程序中運行這些網址。 – Sunny

回答

3

首先,達林的回答讓我開始了,但最終的解決方案還有很多細節,所以我添加了一個單獨的答案。這個很複雜,所以請耐心等待。

有4個步驟,從一個URL獲得的ViewResult:

  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. 領帶一切一起通過實例化新的控制器子類的實例,添加事件處理程序,並調用新的公共執行方法(通過在嘲笑的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. 
      } 
     } 
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管道。不過,我想按照這個線程

How to determine if an arbitrary URL matches a defined route

而且搜索試圖以配合應用程序配置的路由的URL,以儘量減少硬編碼,我碰到它創建的HttpRequest訪問的RouteData對象,但其他線程來需要再次使用反射。

String URL to RouteValueDictionary

+0

你的例子中的網址在哪裏?你似乎在做的是實例化一個控制器並對其進行調用。 OP在他的問題中指出,他有一個url作爲輸入(例如'http:// example.com/Home/Index'),他想要獲得由服務此URL的相應​​控制器操作返回的ActionResult。 –

+0

自從它的MVC應用程序,我認爲控制器和動作名稱可以從URL中提取,並使用foreach循環,控制器和動作名稱可以被替換? – Sunny

+0

你能舉一個例子來說明如何做到這一點嗎? –

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"]; 

現在你已經知道了控制器和動作,你可以使用反射來實例化並執行它。

0

感謝本·米爾斯,這讓我開始與我自己的問題。但是,我發現我不必做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(); 

我希望這可以幫助別人想要實現類似的事情。