This article shows how an ASP.NET Core MVC application can request data using a HTML form so that the browser back button will work. When using a HTTP POST to request data from a server, the back button does not work, because it tries to re-submit the form data. This can be solved by using a HTTP GET, or an AJAX POST.
Code: https://github.com/damienbod/MvcDynamicDropdownList
Request data using a HTTP Post
In an ASP.NET Core MVC application, a user can request data from a server using a HTML form. The form can be set to do a POST request and when the data is returned in the response, it is displayed in the partial view using the data from the POST response.
@model MvcDynamicDropdownList.Models.DdlItems @{ ViewData["Title"] = "Index"; } <h4>Back Fail Confirm Form Resubmission</h4> <form asp-controller="Back" asp-action="SomeDataFromAPost" method="post"> <button class="btn btn-primary" type="submit">Display</button> </form> @if (Model != null) { @await Html.PartialAsync("SomeData", Model) } else { <p>no display items</p> }
The MVC Controller handles the view requests, prepares the data, and returns the views as required.
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using MvcDynamicDropdownList.Models; using System.Collections.Generic; namespace MvcDynamicDropdownList.Controllers { public class BackController : Controller { public IActionResult IndexPost() { return View(); } public IActionResult IndexGet() { return View(); } // SomeDataFromAPost [HttpPost] public IActionResult SomeDataFromAPost() { var model = new DdlItems { Items = new List<SelectListItem> { new SelectListItem { Text = "H1", Value = "H1Value"}, new SelectListItem { Text = "This is cool", Value = "cool"} } }; return View("IndexPost", model); } // SomeDataFromAPostGet [HttpGet] public IActionResult SomeDataFromAGet() { var model = new DdlItems { Items = new List<SelectListItem> { new SelectListItem { Text = "H1", Value = "H1Value"}, new SelectListItem { Text = "This is cool", Value = "cool"} } }; return View("IndexGet", model); } } }
The partial view just displays the data.
@model MvcDynamicDropdownList.Models.DdlItems <p>display items</p> @if (Model.Items != null) { <div style="width: 200px;"> <ol> @foreach (var item in Model.Items) { <li>item.Text</li> } </ol> </div> } else { <p>no display items</p> }
By doing this, when the user clicks the back button, the web application breaks and displays the following message: Confirm Form Resubmission
This is correct, because the browser thinks you are changing the data by doing a POST, or resending an UPDATE data request.
The following gif displays this:
Solving this problem using HTTP GET
The back button problem can be solved by implementing the data request in a different way. The first way would be to send a HTTP GET request, instead of a HTTP POST. Request parameters, if any, are sent in the URL and the back button will then work without problems. This could be implemented as follows:
@model MvcDynamicDropdownList.Models.DdlItems @{ ViewData["Title"] = "Index"; } <h4>Back No Confirm Form Resubmission due to GET Form</h4> <form asp-controller="Back" asp-action="SomeDataFromAGet" method="get"> <button class="btn btn-primary" type="submit">Display</button> </form> @if (Model != null) { @await Html.PartialAsync("SomeData", Model) } else { <p>no display items</p> }
Solving this problem using AJAX HTTP POST
If a POST request is required, it could be sent using AJAX, which can then send a POST request to the server. This is done in ASP.NET Core by using the data-ajax properties, and the jquery.validate.unobtrusive javascript library. Is this example, the DOM element with the content id displays the result.
@{ ViewData["Title"] = "Home Page"; } <h4>Back Ok, No Confirm Form Resubmission due to async</h4> <div> <div> <form asp-controller="Home" asp-action="DynamicDropDownList" data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#content"> <input class="btn btn-primary" type="submit" value="getData" /> </form> <div id="content"></div> </div> </div>
Links:
https://docs.microsoft.com/en-us/aspnet/core/getting-started?view=aspnetcore-2.1&tabs=windows
https://docs.microsoft.com/en-us/aspnet/core/mvc/razor-pages/?view=aspnetcore-2.1&tabs=visual-studio
[…] ASP.NET Core MVC Form Requests and the Browser Back button (Damien Bowden) […]
Not sure you want to recommend replacing a POST with a GET. In many cases, this is dangerous.
The Post-Redirect-Get pattern is another way to fix this issue.
Hi Paul, depends, as long as I don’t change any data, a GET is fine. Updating data , yes never a GET, totally agree
Thanks for the info, Post-Redirect-Get pattern, will add this.
Greetings Damien
[…] ASP.NET Core MVC Form Requests and the Browser Back button – Damien Bowden […]
[…] ASP.NET Core MVC Form Requests and the Browser Back button (Damien Bowden) […]
I don’t usually comment on blogs, but using a GET instead of a POST in a form is wrong for so many reasons. The root reason is that this goes against everything that HTTP was designed to do, and HTTP is what a lot of the internet was designed around.
Sure, you can go back fine. But what if you go back and then forward? Those warnings are there for a reason, and instead of getting one, you’ll submit the form again without knowing it
What if you reload the page after you submit the form? Form submission. Send the link to someone? Another form submission. Search engine crawler fills out the form because it’s not a POST? Another form submission. On your most visited pages? Form submission form submission form submission.
Hi Kyle
This makes sense and I totally agree, will update the blog to make it clear that this is a bad way to fix the back problem.
Greetings Damien