QueryString参数如何绑定到Action方法参数?

4
我有一个WebForms项目,试图运行一些代码,让我可以调用MVC路由并在WebForms页面的正文中呈现结果。
有几个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"。但是我无法指定任何查询字符串参数(例如"/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”中,但是这些值从未绑定到方法参数。

有人知道如何从http请求将QueryString值用于绑定MVC控制器操作吗?

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

我一直在尝试研究内部情况,并将继续这样做。如果我找到任何信息,我会适当地更新此帖子。

我还创建了一个非常简单的Web表单项目,仅包含生成此问题所需的代码,并通过Dropbox共享:https://www.dropbox.com/s/vi6erzw24813zq1/StackMvcGetQuestion.zip 该项目只包含一个Default.aspx页面、一个Controller以及用于呈现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,以查看它接收到了什么数据。 - JTMon
请求是我创建的一个实例,它包装了默认页面default.aspx的传入请求。我进行了调试,并检查了QueryString NameValueCollection,确实包含了变量“foo”,并且已经存在其中。 - ctrlplusb
2个回答

0

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

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

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


我想我正在做你所说的事情。我已经发布了来自我包含的zip项目的示例代码。我试图将名为“variable”的方法参数与同样称为“variable”的查询字符串参数匹配。 “variable”可能不是我参数名称的最明智选择。我已将变量名称更改为“foo”。 :) - ctrlplusb

0

好的,这是一个漫长的调试过程 :) 这将是一个长篇回复,所以请耐心等待 :)

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接