Search code examples
asp.netasp.net-mvcasp.net-corerazor-pages

What's the easiest way to keep existing query strings across posts and redirects in ASP.NET Core razor pages?


I have a ASP.NET Core razor page and model that uses query strings to filter search results and paging via GET.

Consider a user performed a search to arrive at this page:

https://localhost/Users?searchBy=Name&searchTerm=test&resultPage=2

Now on the result page I have a button that performs deletion via a popup modal. When the user clicks delete, it POSTS to a page handler OnPostDelete(). I use the tag helper asp-page-handler for this:

<button asp-page-handler="Delete" asp-route-id="@Model...">

After the deletion, I want the user to be redirected back to the exact same URL as before, so he/she stays on the same result page, instead of being redirected to plain index page.

So my question is how to easily keep these query strings attached throughout the process of posts and redirects? Currently here's what I'm doing:

On the deletion popup modal razor page, I use these codes to capture the current query strings: (I can't figure out how to directly convert Context.Request.Query to a Dictionary)

@{ 
    Dictionary<string, string> query = new Dictionary<string, string>();

    foreach (var queryString in Context.Request.Query)
    {
        query.Add(queryString.Key, queryString.Value);
    }
}

I then add the query to the asp-all-route-data tag helper on the delete button:

<button asp-page-handler="Delete" asp-all-route-data="query" asp-route-id="@Model...">

This makes the resulting formaction attribute generated by the tag helper to contain the query strings in the POST URL.

Then in my page handler's OnPostDelete() method, I re-capture the query strings again, removing the undesirable ones (id etc) added by the delete button, before redirecting:

 Dictionary<string, string> query = new Dictionary<string, string>();
            
 foreach (var queryString in HttpContext.Request.Query)
 {
     if ((queryString.Key != "id") && (queryString.Key != "handler"))
     {
         query.Add(queryString.Key, queryString.Value);
     }
 }

 return RedirectToPage($"Index",query);

It works but seems very tedious. I'm new at this and I'm sure there's a better way to do this. Your help would be greatly appreciated.


Solution

  • This can be solved in a more simple manner.

    Add

    <input type="hidden" asp-for="ReturnUrl" value="@Request.GetEncodedPathAndQuery()" />
    

    Your form may look like this now

    <form method="post">
        <input type="hidden" asp-for="ReturnUrl" value="@Request.GetEncodedPathAndQuery()" />
        <button type="submit" asp-page-handler="Delete" asp-route-id="1">Button 1</button>
        <button type="submit" asp-page-handler="Delete" asp-route-id="2">Button 2</button>
    </form>
    

    Or, if you have a form per button

    <form method="post"
          asp-page-handler="Delete"
          asp-route-id="1"
          asp-route-returnUrl="@Request.GetEncodedPathAndQuery()">
        <button type="submit">Button 1</button>
    </form>
    
    <form method="post"
          asp-page-handler="Delete"
          asp-route-id="2"
          asp-route-returnUrl="@Request.GetEncodedPathAndQuery()">
        <button type="submit">Button 2</button>
    </form>
    

    In Razor page code-behind, add

    [BindProperty]
    public string ReturnUrl { get; set; }
    

    At the end of your POST request:

    return Redirect(ReturnUrl);
    

    Above code is tested and verified on my computer.

    This is a pattern that is often used during login processes:

    • User clicks a link to a resource that requires authentication/authorization
    • User gets redirected to login page
    • User submits login form
    • User gets redirected to resource that requires authentication/authorization