ajax post: how to

Topics: Troubleshooting, Writing modules
May 5, 2011 at 5:46 PM
Edited May 5, 2011 at 5:51 PM

I have one page in my module where I need to do an ajax post. Here are the steps I've taken so far

Route: created a route for the controller/Submit action for the submit

Controller: created the action that does a simple return without any processing at this point

public ActionResult Submit(string string1, string string2)
{
return Json(new { success = true, message = "Success" }, JsonRequestBehavior.AllowGet);
}

View: created the POST method as follows

$.ajax(
{
    type: "POST",
    url: "/controller/Submit",
    data: {
        string1: "1",
        string2: "2"
    },
    dataType: "Json",
    success: function (result) {
        alert("success");
    },
    error: function (req, status, error) {
        alert(error);
    }
});

 

When the script runs, the error alert I get back says "Internal Server Error" which is not giving me any clue. If I attempt to hit the action directly by browsing to /controller/Submit, I get a return message on the screen as such

{"success":true,"message":"Success"}

so I know the action is running, my route is working and it's returning a result...so I'm thinking I'm not calling the POST properly.

Any suggestions on how to write the POST method so it works within Orchard? Thanks for the help!

May 5, 2011 at 5:59 PM

I you want your action to accept POST instead of GET, just use the [HttpPost] attribute.

May 5, 2011 at 6:09 PM

I tried adding the [HttpPost] attribute, but I got the same result

I ran through it in Debug mode and it stopped on the whole anti-forgery thing noted here: http://weblogs.asp.net/dixin/archive/2010/05/22/anti-forgery-request-recipes-for-asp-net-mvc-and-ajax.aspx

I guess I'll try to add this JQuery script and run with it to see what happens.

Coordinator
May 5, 2011 at 6:18 PM

You might need to generate the anti forgery token. There is an example in the Stars module on the gallery.

May 5, 2011 at 6:44 PM

Can you point me to where the example is in the Stars module? The only thing I can find is in the Parts.Stars.cshtml file where they're doing a using(Html.BeginAntiForgeryPost()) method.  Is that what you're referring to and can that be used in a JQuery ajax post?

Thanks for the help

May 5, 2011 at 7:08 PM
Edited May 5, 2011 at 7:17 PM

I'm trying to go the route posted here http://orchard.codeplex.com/discussions/249394, since it seemed to work for the poster. The steps I've taken are

1. Added @Html.AntiForgeryTokenOrchard() to the top of my View which created a hidden input

2. In my JavaScript, I added the following lines

 

var json = JSON.stringify("string1", "string2");
var token = { "__RequestVerificationToken" : $("input[name=__RequestVerificationToken]").val() };
json = $.appendAntiForgeryToken(json, token);

 

3. Changed the data parameter in my script to use the json variable created above.

4. I'm including the JQuery script at the above link that has the $.appendAntiForgeryToken() method in it.

5. Changed my Action to include [HttpPost] attribute and removed the JsonRequestBehavior.AllowGet that was at the end.

Still no luck and it dies with the message "A required anti-forgery token was not supplied or was invalid."

I'll keep trying...I emailed the author of the post I referenced as well to see if he can help.

May 5, 2011 at 7:54 PM

I GOT IT!

so...did away with that whole JQuery plugin and simply added __RequestVerificationToken: $("input[name=__RequestVerificationToken]").val() to the end of my data parameters. I had to remove dataType and contentType as well.

Oct 28, 2011 at 5:19 AM

 

Hi,
  I'm having the exact same problem. When I disable the AntiForgery in the Module.txt everything works. But...

  I've tried everything I can imagine but still not getting it to work. I get an exception at :     validator.OnAuthorization(filterContext);    -->(A required anti-forgery token was not supplied or was invalid.)

 

  Would you be able to let  me know what I'm missing in my code? Or post what did work for you?  Either way thank you very much.


CONTROLLER ACTION:  (note the ContactDetailsRecord type in the Action arguments)

[HttpPost] 
public ActionResult Add(ContactDetailsRecord newContactItem)        {            
      var a = newContactItem;
      return null;
}

 AJAX CALL:

            $('#AddNewContact').click(function () {

                var token = $('input[name=""__RequestVerificationToken""]').val();

                var newContactItem = {
                    ManagersName: "333",
                    Telephone: "4444",
                    AdInsightCode: "555",
                    __RequestVerificationToken: $("input[name=__RequestVerificationToken]").val() //Does this go here??
                };

                var data2Send = JSON.stringify(newContactItem);


                var headers = {}; // As recommended by Phil Haack in http://haacked.com/archive/2011/10/10/preventing-csrf-with-ajax.aspx
                // other headers omitted
                headers['__RequestVerificationToken'] = token;

                $.ajax({
                    type: "POST",
                    //headers: headers,
                    url: '/admin/ContactDetails/AddContact',
                    data: data2Send,
                    //contentType: "application/json; charset=utf-8",
                    //dataType: "json",
                    success: function () {
                        alert('success');
                    },
                    error: function () {
                        alert('error');
                    }
                });

Coordinator
Oct 28, 2011 at 5:41 AM

A few things: first, you need to output that hidden input with the token, otherwise, your script will have nothing to copy, as I'm sure you'll have noticed by debugging into this code. Second, there is no need to stringify yourself, jQuery will do that for you just fine. Third, you don't need to set the header.

I was able to make ajax calls work with this hidden field:

<input id="__requesttoken" type="hidden" value="@Html.AntiForgeryTokenValueOrchard()" />

And with a jQuery call like this:

$.post("theActionUrl",
    {
        foo: bar,
        __RequestVerificationToken: $("#__requesttoken").val()
    },
    function(data) {
        // ...
    });

Oct 28, 2011 at 11:07 AM

Thanks a lot Bertrand. You were absolutely right.

As recommended I included in the markup:

<input id="__requesttoken" type="hidden" value="@Html.AntiForgeryTokenValueOrchard()" />

the working version for the ajax call is as follows:

            $('#AddNewContact').click(function () {

                var newContactItem = {
                    ContactDetailsContainerId : '@(Model.ComponentId)',
                    ManagersName: $("#newContactName").val(),
                    Telephone: $("#newContactTelephone").val(),
                    AdInsightCode: $("#newContactAdInsightCode").val(),
                    __RequestVerificationToken: $("#__requesttoken").val()
                };

                $.ajax({
                    type: "POST",
                    url: '/admin/ContactDetails/AddContact',
                    data: newContactItem,
                    //contentType: "application/json; charset=utf-8",
                    //dataType: "json",
                    success: function () {
                        newContactAdded();
                    },
                    error: function () {
                        alert('error');
                    }
                });
            });

 I left on pourpose commented the contentType and dataType as the json type was "translating" the token.

 

 Thank you very much again

Nov 7, 2011 at 2:08 PM

I have been working with this lately, and I thought I should post my findings since this thread has been hugely helpful to me:

I have found that, taking the above post as an example, specifying dataType: 'json', or altering the contentType doesn't seem to force your object to be sent as json. This seems to be assumption people make about using jQuery ajax, and is totally wrong. Firstly dataType, as per the JQuery docs, specifies to RESULT data type, not what you are sending. Furthermore setting the contentType doesn't force the production of json either, but it will break processing on the server side as the model binder expects json and doesn't find it if you are not using JSON.stringify.

So sending the object like in the above example send's it as form data, where the token is clearly readable, and therefor works.

Nov 10, 2011 at 12:46 PM

Hi,

Could you show how made form?

Sep 6, 2012 at 5:24 AM

I just ran into this issue and got it working using Bertrand's technique.  However, it was only working for IE and Firefox for me.  Chrome was failing.  I had to add a <machineKey> to Web.Config.  Once I did that, it worked on all browsers.