Continually receiving 400 (Bad Request) on jquery ajax post to MVC controller

You are sending a completely broken and invalid JSON string to the server. It's normal that the controller action rejects it. In addition to that you have put into comments the contentType parameter specifying that you want to send a JSON request.

So here's the correct way to do the request:

$.ajax({ //actually approve or reject the promotion
    url: url,
    type: "POST",
    data: JSON.stringify({ 
        // Those property names must match the property names of your PromotionDecision  view model
        promotionId: data.PromotionId, 
        userId: data.UserId, 
        reasonText: data.ReasonText
    }),
    contentType: "application/json; charset=utf-8",
    success: function (data) {
        if (indicator == 'A') {
            alert('Promotion approved successfully!');
        }
        else {
            alert('Promotion rejected successfully.');
        }

        var homelink = '<%: Url.Action("Index","Home") %>';
        window.location.href = (homelink);

        returndata = data;
    },
    error: function (xhRequest, ErrorText, thrownError) {
        alert("Failed to process promotion correctly, please try again");
        console.log('xhRequest: ' + xhRequest + "\n");
        console.log('ErrorText: ' + ErrorText + "\n");
        console.log('thrownError: ' + thrownError + "\n");
    }
});

Notice how I am using the JSON.stringify method that is natively built-into modern browsers to ensure that the JSON being sent to the server is correctly and all values are properly encoded. And if you need to support browsers from the stone age, you could include the json2.js script to your page which will define the JSON.stringify method.

Important remark: Absolutely never build JSON strings using string concatenations as in your code.

Alternatively if you don't want to send a JSON request you could send a standard application/x-www-form-urlencoded request:

$.ajax({ //actually approve or reject the promotion
    url: url,
    type: "POST",
    data: { 
        promotionId: data.PromotionId, 
        userId: data.UserId, 
        reasonText: data.ReasonText
    },
    success: function (data) {
        if (indicator == 'A') {
            alert('Promotion approved successfully!');
        }
        else {
            alert('Promotion rejected successfully.');
        }

        var homelink = '<%: Url.Action("Index","Home") %>';
        window.location.href = (homelink);

        returndata = data;
    },
    error: function (xhRequest, ErrorText, thrownError) {
        alert("Failed to process promotion correctly, please try again");
        console.log('xhRequest: ' + xhRequest + "\n");
        console.log('ErrorText: ' + ErrorText + "\n");
        console.log('thrownError: ' + thrownError + "\n");
    }
});

This should work the same way and the controller action should be able to properly bind the model.

Remark: I have noticed that you used the following line in your success callback: returndata = data;. This leads me to believe that you are somehow attempting to consume the result of an asynchronous AJAX request outside of the success callback which is not possible. I have no idea what you are doing with this returndata variable but I feel it is wrong.


This error also occurs when using [ValidateAntiForgeryToken] attribute for MVC action method in combination with Ajax call.

Check this out, might help as well: https://stackoverflow.com/a/60952294/315528