Search code examples
asp.net-core-mvcquery-stringasp.net-core-8

QueryString is empty during ASP.NET Core 8 MVC form post


Note: the answer may be in either VB.NET or C#. I have no preference for this Q&A.

I'm following this example to build a simple ASP.NET Core 8 MVC application that uses cookie authentication. I'm running into a problem where the query string on the URL isn't being sent to the controller. The value for Request.QueryString is an empty string.

Here's my URL:

https://localhost:55387/Account/SignIn?ReturnUrl=%2FContacts

Here's my controller:

Public Class AccountController
  <HttpPost>
  Public Async Function SignInAsync(Setting As Setting) As Task(Of IActionResult)
    Dim ReturnUrl As String = Me.Request.Query("ReturnUrl")

    ' etc.
  End Function
End Class

...and here's my view:

@model Db.Models.Setting

@{
  ViewData["Title"] = "Sign In";
}

<h1>Sign In</h1>
<h4>Enter the site PIN</h4>
<hr />
<div class="row">
  <div class="col-md-4">
    <form asp-action="SignIn">
      <div asp-validation-summary="ModelOnly" class="text-danger"></div>

      <div class="form-group col-5">
        <label asp-for="AuthPin" class="control-label"></label>
        <input id="authPin" asp-for="AuthPin" class="form-control" />
        <span asp-validation-for="AuthPin" class="text-danger"></span>
      </div>

      <div class="form-group">
        <input id="signInButton" type="submit" class="btn btn-primary" value="Sign In" disabled />
      </div>
    </form>
  </div>
</div>

The trouble is that the example is for a Razor Pages application. Mine is MVC. In the example, this works just fine:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
  this.ReturnUrl = returnUrl;
}

Here, the returnURL variable is populated as expected. But in MVC, we don't have an OnPostAsync function. I'm handling everything in the controller action. And the action isn't picking up the query string for some reason.

There's this answer, but in my case there is no Url property on the Request instance. There is a Path property, but its value doesn't contain a query string ("/Account/SignIn").

There's also this answer, but adding a ReturnUrl parameter on the SignIn post action didn't help.

How can I get my controller/action to collect the query string on form POST?


Solution

  • I was able to work around the problem using this technique. It's not the prettiest solution, but it works at least.

    In the view:

    @Html.Hidden("QueryString", @Context.Request.QueryString)
    

    ...and then in the controller:

    <HttpPost>
    Public Async Function SignInAsync(QueryString As String) As Task(Of IActionResult)
      Dim oQueryString As NameValueCollection
      Dim oDictionary As RouteValueDictionary
      Dim sReturnUrl As String
      Dim oResult As IActionResult
    
      oQueryString = HttpUtility.ParseQueryString(QueryString)
      oDictionary = New RouteValueDictionary
      sReturnUrl = oQueryString("ReturnUrl")
    
      Array.ForEach(oQueryString.AllKeys, Sub(Key) oDictionary.Add(Key, oQueryString(Key)))
    
      oResult = Me.RedirectToAction("SignIn", oDictionary)
    
      ' etc.
    End Function
    

    That did the trick.