Redirect after POST doesn't change URL - asp.net-mvc

I'm using the "Redirect After Post" (http://en.wikipedia.org/wiki/Post/Redirect/Get) pattern to solve the problems with refreshing that it solves, but I'm not seeing the URL change after the POST and subsequent GET.
Here is my setup:
I have a form with some pretty extensive client-side validation, then submit.
#using (Html.BeginForm("AddItem", "Order", FormMethod.Post, new { Id = "addItemForm" }))
{
// form stuff
}
Client-side validation:
$('#addToOrder').click(function () {
// do a bunch of validation stuff.
}
if (criteriaMet) {
$('#addItemForm').submit();
}
"AddItem" controller:
public class OrderController {
[HttpPost]
public ActionResult AddItem(long? orderId, long menuItemId)
{
if (oneConditionIsTrue)
{
return RedirectToRoute("NamedRoute1", new { RouteValueDictionary values });
}
else
{
return RedirectToRoute("NamedRoute2", new { RouteValueDictionary values });
}
}
public class NamedRouteController
{
public ActionResult NamedRouteAction
{
// do some stuff
if (mobile)
{
return View("MobileView", model);
}
else
{
return View("RegularView", model);
}
}
After redirecting from the POST action (AddItem), I can step things through the GET action to the return (either one). I would expect the URL in the browser after all of this to be http://mydomain.com/NamedRoute/NamedRouteAction but it's http://mydomain.com/Order/AddItem. Why is this? Shouldn't the RedirectToRoute change the URL?
What am I missing?

I suspect that the controller action is somehow invoked with an AJAX request. For example this could happen if you are using jQuery Mobile or something. Or maybe there's some other script that you have written doing this - it hijacks the form submission and sends an AJAX request instead. And since it is an AJAX request, you cannot possibly expect that the url in the client browser would ever change - that's the whole point of AJAX - stay on the same page.
This could be very easily verified by using a javascript debugging tool such as FireBug. Simply look at the Network tab and see if the POST request was an AJAX request. In the Net tab find the request and see if there's the following request header:
X-Requested-With: XMLHttpRequest
jQuery appends this HTTP header to all AJAX requests it sends.
So basically if you expect a redirect to happen after a POST request you shouldn't use AJAX to submit your form. Or to be more precise: the redirect happens on the server side (once again you will be able to see it in FireBug - the 302 status code) and then the XMLHttpRequest simply follows this redirect but the client browser will not change its current location.

Related

Asp.Net Core Unobtrusive Ajax throwing 400 error on live server

My Every form is of type
<form id="addform" asp-area="Admin" asp-controller="Departments" asp-action="Add" data-ajax="true" data-ajax-method="post" data-ajax-success="onSuccess" data-ajax-failure="onFailure" data-ajax-begin="onBegin">
</form>
I have also appended RequestVerificationToken on every ajaxSend request
$(document).ajaxSend(function (e, xhr, options) {
debugger;
if (options.type.toUpperCase() == "POST") {
var token = $("input[name='__RequestVerificationToken']").val();
xhr.setRequestHeader("__RequestVerificationToken", token);
}
});
My controllers are like this
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Add(DepartmentViewModel departmentViewModel)
{
return View();
}
Now, it is working fine locally but not working correctly on the live server.
Some requests are working fine and after few request it return 400 bad request error.
I have tried many things but all in vain. I need this security otherwise I would have skipped the same
The header name should be RequestVerificationToken without the leading underscores. The version with the underscores is the name you should use if you are posting it as a form value. You can configure this to something else:
https://www.learnrazorpages.com/security/request-verification#configuration

return Json will redirects to another view when Url specified

When you do return Json(...) you are specifically telling MVC not to use a view, and to serve serialized JSON data. Your browser opens a download dialog because it doesn't know what to do with this data.
i got the above information from below link
How to return Json object from MVC controller to view
when i give this below code in some action result
return Json(new { Url = "sep/employee"}
whether it will redirect to specified action ? how it redirects to the URl ?
for this case why i cant use return RedirectToAction("sep/employee").
how return Json code redirects to action which specified in the Url.
ex:
public ActionResult Index()
{
................................
return Json(new { Url = "sep/employee"}
}
public ActionResult employee()
{
....................
}
what is the difference b/s redirectaction and return Json
You return the following line to an ajax success call
return Json(new { Url = "sep/employee"});
you then need to specify where to redirect to the new page
success: function(result) {
window.location.href=result.Url;
}
RedirectToAction simply returns 302 code to your browser with URL telling where the browser should redirect to. The browser immediately makes yet another call to the server to that URL obtained from redirection response.
RedirectToAction internals are:
Construct redirection url from parameters passed to RedirectToAction
Return new RedirectToRouteResult
In ExecuteResult of RedirectToRouteResult you can find the following line:
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
which is merely returning 302 with redirection URL. More info - look at source code here.
Returning JSON data simply returns JSON serialized object to your browser. Is not meant to do any redirect. To consume such a result you will likely call the server using $.ajax:
$.ajax({
url: 'sep/employee',
type: 'POST'
success: function(result) {
// handle the result from the server, i.e. process returned JSON data
}
});
ExecuteResult of JsonResult just serializes passed CLR object to the response stream, which lands in your browser after response is fully received. Then you can handle such response in JavaScript code.
EDIT:
You of course can mimic 302 redirection by returning Json like
return Json(new { Url = "redirectionUrl"}
and at client side handle it like
$.ajax({
url: 'sep/employee',
type: 'POST'
success: function(result) {
// mimic the 302 redirection
windows.location.href = result.Url
}
});
although IMHO it should be avoided since you reinvent MVC infrastructure and enlarge your code base.
whether it will redirect to specified action ? how it redirects to the URl ?
I assume you mean to ask, "will it redirect to specified action? how will it redirect the the URI?"
To answer your question: How it redirects to the URL?
In your example it will redirect to the URL, if you made the HTTP request as AJAX and that you will explicitly handle the resulting HTTP response to actually redirect to the URL that you received. So your view should have something like this:
$.ajax({
url: '{your controller}/index',
type: 'GET'
success: function(url) {
// write code to redirect to the URL
// example:
// window.navigate(url)
}
});
If your view does not have anything that, then it will not redirect. You did not post your view, so I am assuming it just a normal page load.
Your next question, what is the difference b/s redirection and return Json?
If you really just want to redirect then do RedirectToAction from your controller, that is exactly what it is for. You can do the same effect using AJAX and JSON to be able to redirect, but AJAX and JSON serves a much wider purpose than just redirecting and in most cases (unless you have very good reasons) you probably will not want replace RedirectToAction with that approach.

MVC - Identify Page Form Authentication time out

we are developing MVC3 application such that most of our action methods are called via ajax calls and return partialviews. we come across a situation where we need to identify if the action method is called from Form Authentication time out.
public ActionResult LogOn()
{
// I want to return View("LogOn"); if the call is coming from
// Form Authentication time out
return PartialView(Model);
}
here is my web.config looks like:
<authentication mode="Forms">
<forms loginUrl="~/Home/LogOn" timeout="20" />
</authentication>
Appreciate your input.
Your action will never be hit if the authentication cookie has timed out. The forms authentication module directly redirects to the logon page. One possibility for you to detect this happening from client scripting is to set a custom HTTP header in the controller action serving this logon page:
public ActionResult LogOn()
{
var model = ...
Response.AppendHeader("X-LOGON", "true");
return View(model);
}
and then when performing your AJAX request you could use the getResponseHeader method on the XHR object in order to verify if the X-LOGON header was set meaning that the server redirected to the logon page. In this case in your success AJAX handler instead of simply injecting the server response into the DOM or relying on the returned JSON you could show some alert message informing the user that his authentication session has timed out and he needs to login again. Another possibility is to automatically redirect him to the logon page using the window.location.href method:
$.ajax({
url: '/home/some_protected_action',
success: function (data, textStatus, XMLHttpRequest) {
if (XMLHttpRequest.getResponseHeader('X-LOGON') === 'true') {
// the LogOn page was displayed as a result of this request
// probably timeout => act accordingly
}
}
});
There is no way from the server to distinguish between the user loading the page normally versus performing a page refresh.
There are ways to tell the difference between a regular request and an AJAX request, but it doesn't sound like that's what you're asking for.
There is no easy way but if you apply Post-Redirect-Get, I am not sure you will have that problem.

Redirect after EndInvoke in ASP.NET MVC

Hi have the following code on my view (JQuery):
$.post('<%=Url.Content("~/Customer/StartLongRunningProcess")%>');
Wich invokes an asynchronous call (C#):
public void StartLongRunningProcess()
{
ProcessTask processTask = new ProcessTask();
processTask.BeginInvoke(new AsyncCallback(EndLongRunningProcess), processTask);
}
Finally, the result of the call:
public void EndLongRunningProcess(IAsyncResult result)
{
ProcessTask processTask = (ProcessTask)result.AsyncState;
string id = processTask.EndInvoke(result);
RedirectToAction("~/SubscriptionList/SubscribedIndex/" + id);
}
The redirect is ignored. Response.Redirect also fails, since the HTTP headers has been already sent. I've tried change the window.location from javascript, this works, but I'm unable to pass the parameter id by ViewData. Any idea to resolve this?
Are you sure the headers have already been sent? I'm not really up on asynchronous controllers, but I would doubt that it would start sending any headers right away. My first thought would be that a redirect response to an ajax call isn't handled by the browser. You will probably need to implement some logic that sends back a result with the URL and have your success delegate in jQuery look for that piece of data and then do the redirect through javascript (i.e. window.location).
HTH

What is the correct response to an HTTP POST request?

For a POST method, the W3 specs say:
If a resource has been created on the origin server, the response
SHOULD be 201 (Created) and contain an entity which describes the
status of the request and refers to the new resource, and a Location
header (see Section 10.4).
http://www.ietf.org/internet-drafts/draft-ietf-httpbis-p2-semantics-05.txt (section 8.5)
The standard response actually seems to be to send a Redirect to the newly created resource.
I'm building my site with ASP.NET MVC, and tried to follow the spec, so created a ResourceCreatedResult class:
public class ResourceCreatedResult : ActionResult
{
public string Location { get; set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.StatusCode = 201;
context.HttpContext.Response.ClearHeaders();
context.HttpContext.Response.AddHeader("Location", Location);
}
}
And my action looks something like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateNew(string entityStuff)
{
Entity newEntity = new Entity(entityStuff);
IEntityRepository entityRepository = ObjectFactory.GetInstance<IEntityRepository>();
entityRepository.Add(newEntity);
ActionResult result = new ResourceCreatedResult()
{ Location = Url.Action("Show", new { id = newEntity.Id }) };
return result;
}
However, IE, Firefox and Chrome all fail to redirect to the new resource. Have I messed up generating the correct response, or do web browsers not expect this type of response, instead relying on servers to send a Redirect response?
To be explicit, browsers (including modern browsers like Firefox 3 and IE8) do not "take the hint" and follow up an HTTP 201: Created response with a GET request to the URI supplied in the Location header.
If you want browsers to go to the URI supplied in the Location header, you should send an HTTP 303: See Other status instead.
Redirect after post or post/redirect/get is something your application must do to be user friendly.
Edit. This is above and beyond the HTTP specifications. If we simply return a 201 after a POST, the browser back button behaves badly.
Note that Web Services requests (which do NOT respond to a browser) follow the standard completely and do NOT redirect after post.
It works like this.
The browser POSTS the data.
Your application validates the data. If it's invalid, you respond with the form so they can fix it and POST.
Your application responds with a redirect.
The browser gets the redirect and does a GET.
Your application sees the GET and responds.
Now -- hey presto! -- the back button works.
My solution is to respond with a '201 Created' containing a simple page with a link to the new resource, and a javascript redirect using location.replace().
This lets the same code work for API and browser requests, plays nicely with Back and Refresh buttons, and degrades gracefully in old browsers.
As stated in the spec the response SHOULD be a HTTP 201 with redirect. So it isn't mandatory for a browser vendor to implement the correct answer...
You should try to change to a 30x code to see if it is correctly redirected. If so, it's a browser problem, else it may come from your code (I don't know anything in ASP.NET so I can't "validate" your code)
Shouldn't that only count for when something is "Created" and therefore a simple redirect to action should be genuinely sufficient?

Resources