【问题标题】:How do QueryString parameters get bound to Action method parameters?QueryString 参数如何绑定到 Action 方法参数?
【发布时间】:2013-05-21 10:35:00
【问题描述】:

我有一个 webforms 项目,并且正在尝试运行一些代码,以允许我调用 MVC 路由,然后在 web 表单页面的正文中呈现结果。

我使用几个 HttpResponse/Request/Context 包装器来执行对 MVC 路由的调用,例如:

private static string RenderInternal(string path)
{
  var responseWriter = new StringWriter();

  var mvcResponse = new MvcPlayerHttpResponseWrapper(responseWriter, PageRenderer.CurrentPageId);
  var mvcRequest = new MvcPlayerHttpRequestWrapper(Request, path);
  var mvcContext = new MvcPlayerHttpContextWrapper(Context, mvcResponse, mvcRequest);

  lock (HttpContext.Current)
  {
    new MvcHttpHandlerWrapper().PublicProcessRequest(mvcContext);
  }

  ...

代码可以很好地执行简单的 MVC 路由,例如“/首页/索引”。但是我不能指定任何查询字符串参数(例如“/Home/Index?foo=bar”),因为它们只是被忽略了。我尝试直接在 RequestWrapper 实例中设置 QueryString,如下所示:

public class MvcPlayerHttpRequestWrapper : HttpRequestWrapper
{
  private readonly string _path;
  private readonly NameValueCollection query = new NameValueCollection();

  public MvcPlayerHttpRequestWrapper(HttpRequest httpRequest, string path)
    : base(httpRequest)
  {
    var parts = path.Split('?');

    if (parts.Length > 1)
    {
      query = ExtractQueryString(parts[1]);
    }

    _path = parts[0];
  }

  public override string Path
  {
    get
    {
      return _path;
    }
  }

  public override NameValueCollection QueryString
  {
    get
    {
      return query;
    }
  }

  ...

调试时,我可以看到正确的值在“request.QueryString”中,但这些值永远不会绑定到方法参数。

有谁知道如何使用 QueryString 值并将其从 http 请求绑定到 MVC 控制器操作?

QueryString 值的处理似乎比我预期的要复杂。我对 MVC 请求管道的内部了解有限。

我一直在尝试自己研究内部结构,并将继续这样做。如果我发现任何东西,我会适当地更新这篇文章。

我还创建了一个非常简单的 Web 表单项目,其中仅包含产生此问题所需的代码,并已通过保管箱共享它:https://www.dropbox.com/s/vi6erzw24813zq1/StackMvcGetQuestion.zip 该项目仅包含一个 Default.aspx 页面、一个控制器和用于呈现 MVC 路径结果的 MvcWrapper 类。如果您查看 Default.aspx.cs,您将看到一个包含查询字符串参数的路由路径被传入,但它永远不会绑定到操作上的参数。

作为快速参考,以下是该 Web 项目的一些摘录。

控制器:

public class HomeController : Controller
{
  public ActionResult Index(string foo)
  {
    return Content(string.Format("<p>foo = {0}</p>", foo));
  }
}

Default.aspx 页面:

protected void Page_Load(object sender, EventArgs e)
{
  string path = "/Home/Index?foo=baz";

  divMvcOutput.InnerHtml = MvcWrapper.MvcPlayerFunctions.Render(path);
}

我已经为此苦苦挣扎了很长一段时间,因此我将不胜感激任何形式的建议。 :)

【问题讨论】:

  • 我假设您可以调试 MVC 方法?如果是,则在收到请求时检查请求的URL,以及控制器的ValueProvider,看看它收到了什么数据。
  • 这个请求是我创建的一个实例,它包装了 webforms default.aspx 页面的传入请求。我调试并检查了 QueryString NameValueCollection 实际上确实包含其中的“foo”变量,确实如此。

标签: asp.net asp.net-mvc c1-cms


【解决方案1】:

MVC 框架将尝试从查询字符串(以及其他可用数据,如发布的表单字段等)中填充操作方法的参数值,这部分你做对了。您错过的部分是它通过将参数名称与传入的值名称匹配来实现这一点。因此,如果您在 Controller MyController 中有一个带有签名的 MyMethod 方法:

public ActionResult MyMethod(string Path)
{
    //Some code goes here
}

查询字符串(或其他变量来源之一)必须包含一个名为“Path”的变量,以便框架能够检测到它。查询字符串应该是 /MyController/MyMethod?Path=Baz

【讨论】:

  • 我想我正在做你所说的。我已经发布了我包含的 zip 项目中的示例代码。我正在尝试将名为“variable”的方法参数与也称为“variable”的查询字符串参数相匹配。 “变量”可能不是作为参数名称的最明智选择。我已将变量名称更改为“foo”。 :)
【解决方案2】:

好的。这是一个很长的调试会话 :) 这将是一个很长的响应,所以请耐心等待 :)

首先是 MVC 的工作原理。当您使用输入参数调用操作方法时,框架将调用一个名为“DefaultModelBinder”的类,该类将尝试为每个基本类型(int、long 等)和复杂类型(对象)的实例提供一个值。此模型绑定器将依赖于称为 ValueProvider 集合的东西来查找查询字符串、提交的表单等中的变量名称。我们最感兴趣的 ValueProvider 之一是 QueryStringValueProvider。您可以猜到,它获取查询字符串中定义的变量。在框架的深处,此类调用 HttpContext.Current 来检索查询字符串的值,而不是依赖于传递给它的值。在您的设置中,这导致它看到带有 localhost:xxxx/Default.aspx 的原始请求作为底层请求,导致它看到一个空的查询字符串。实际上,在 Action 方法(在您的情况下为 Bar )中,您可以获得值 this.QueryString["variable"] 并且它将具有正确的值。 我修改了 Player.cs 文件以使用 Web 客户端来调用在 VS 的单独副本中运行的 MVC 应用程序,它运行良好。所以我建议你单独运行你的 mvc 应用程序并调用它,它应该可以正常工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-21
    • 2016-05-06
    相关资源
    最近更新 更多