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

Advertisements

5 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) […]

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 )

Google+ photo

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

Twitter picture

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

Facebook photo

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

w

Connecting to %s

%d bloggers like this: