ASP.NET Core MVC Form Requests and the Browser Back button

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

7 comments

  1. […] ASP.NET Core MVC Form Requests and the Browser Back button (Damien Bowden) […]

  2. 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.

    1. 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

  3. […] ASP.NET Core MVC Form Requests and the Browser Back button – Damien Bowden […]

  4. […] ASP.NET Core MVC Form Requests and the Browser Back button (Damien Bowden) […]

  5. 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.

    1. 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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: