views:

117

answers:

2

I am using the post-redirect-get pattern for all my forms, but now need to add AJAX functionality to improve the user experience. My initial thoughts are that the two do not mix.

In the PRG scenario, I would have my post action that would then either redirect back to my get action if there is a validation error, or redirect to my success get action otherwise.

In the AJAX scenario, I need to return a partial view either way. More typically, I would check to see if it is an AJAX request first. If so, return the partial view, else return the view.

Any thoughts or suggestions?

A: 

Well, part of the reason for PRG is to avoid the "Do you want to resubmit the form?" dialog. AJAX typically doesn't have this problem, since you aren't really doing a 'POST' (you are, but you catch my drift).

Anyways, Rails supports what you'd want to do, and I don't think it would be that rough translating it to ASP.NET MVC.

First, you can tell your client side JavaScript to send AJAX as text/javascript. Using jQuery:

jQuery.ajaxSetup({
    'beforeSend': function(xhr) { xhr.setRequestHeader("Accept", "text/javascript"); }
});

Put that somewhere before the AJAX is run. This will affect all other AJAX calls, but shouldn't do anything unintended unless you're looking for it on the backend.

Then, in your controller, look for the text/javascript header and respond accordingly. Check out this SO question, which references this article. Wish I could give more info, but I don't have VS handy to try it out right now.

swilliams
+1  A: 

We use Post-Redirect-Get in our app. Here's the essence of what we do, which hinges around the Request.IsAjaxRequest() method and splitting your views into .aspx each of which host an .ascx so that each can action can be called both synchronously and asynchronously (i.e. via Ajax).

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Foo foo)
{
    try
    {
        // Save the changes to the data store
        unitOfWork.Foos.Attach(foo);
        unitOfWork.Commit();

        if (Request.IsAjaxRequest())
        {
            // The name of the view for Ajax calls will most likely be different to the normal view name 
            return PartialView("EditSuccessAsync");
        }
        else
        {
            return RedirectToAction("EditSuccess");
        }
    }
    catch (Exception e)
    {
        if (Request.IsAjaxRequest())
        {
            // Here you probably want to return part of the normal Edit View
            return PartialView("EditForm", foo);
        }
        else
        {
            return View(foo);
        }
    }
}

We have a slight variant on this as well where we specifically catch RulesException's (from the xVal in order to treat model validation errors differently to other 'more serious' exceptions.

catch (RulesException re)
{
    re.AddModelStateErrors(ModelState, "");

    return View(foo);
}

Saying all that though, sometimes I get a sneaking suspicion that we might be doing it slightly wrong.

Phil Peace
You can abstract out the if ajax... logic into an AjaxView method in a base class to remove clutter. The main problem with your approach is that you are returning a view from the POST action so it is not PRG in the case of a validation failure. I redirect back to the original GET action, using an export and import of ModelState. The main problem is that FireFox does not preserve the ajax flag between redirects, so in my GET action, IsAjaxRequest is false. I currently have to work around this with another action filter that preserves the flag to tempdata. Not very elegant though.
zaph0d