I am building an Orchard CMS module that I am testing on an Orchard 1.3.10 site. The module displays a details view for one of my entities and I have a "favorites" button that I would like to click and do an ajax post to a controller action to save the entity as a favorite in the database.
On the view I have the following code:
<div style="padding: 10px;">
<span data-id="@Model.Id" id="addFavorite" style="cursor: pointer;">
[Add Favorite]
</span>
</div>
<script type="text/javascript">
$("#addFavorite").click(function () {
alert("here we go...");
$.ajax({
type: "post",
dataType: "",
url: "/orchardlocal/mymodule/stuff/AddFavorite",
data: { id: $(this).data("id") },
success: function (response) {
alert("it worked");
}
});
});
</script>
My controller action is...
[HttpPost]
public ActionResult AddFavorite(int id)
{
return View();
}
When I run the site without being logged into Orchard, this code posts back just fine. If I log in and click on Add Favorite, I get this exception...
A required anti-forgery token was not supplied or was invalid.
System.Web.Mvc.HttpAntiForgeryException was unhandled by user code Message=A required anti-forgery token was not supplied or was invalid. Source=System.Web.WebPages ErrorCode=-2147467259 WebEventCode=0 StackTrace: at System.Web.Helpers.AntiForgeryWorker.Validate(HttpContextBase context, String salt) at System.Web.Helpers.AntiForgery.Validate(HttpContextBase httpContext, String salt) at System.Web.Mvc.ValidateAntiForgeryTokenAttribute.OnAuthorization(AuthorizationContext > filterContext) at Orchard.Mvc.AntiForgery.AntiForgeryAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext) in C:\Code\OrchardDev2\src\Orchard\Mvc\AntiForgery\AntiForgeryAuthorizationFilter.cs:line 37 at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) InnerException:
Why does it treat the post differently when logged in and not?
How can I supply an anti-forger token to avoid this?
Thanks, Brian
ASP.NET MVC doesn't support the generation of raw anti-forgery tokens by default. Fortunately Orchard provides an extension method for that.
You can simply change your ajax call as is:
$.ajax({
type: "post",
dataType: "",
url: "/orchardlocal/mymodule/stuff/AddFavorite",
data: {
id: $(this).data("id") },
__RequestVerificationToken: '@Html.AntiForgeryTokenValueOrchard()'
},
success: function (response) {
alert("it worked");
}
});
This technique is useful as you don't need an existing FORM on your page. Though this solution is only valid if the javascript is rendered from a Razor view.
There is still a solution if you have a separate script file from your view, which is to save the anti-forgery token inside a javascript variable declared from the view, then use it from the script:
@using(Script.Head()) {
<script type="text/javascript">
//<![CDATA[
var antiForgeryToken = '@Html.AntiForgeryTokenValueOrchard()';
//]]>
</script>
}
Then from the script:
data: {
id: $(this).data("id") },
__RequestVerificationToken: antiForgeryToken
}
If not, then the solution proposed by Darin would be he correct approach.
How can I supply an anti-forger token to avoid this?
This will depend on where the hidden field containing the anti forgery token is located on the page. For example:
$("#addFavorite").click(function () {
var token = $(':input[name="__RequestVerificationToken"]').val();
$.ajax({
type: "post",
dataType: "",
url: "/orchardlocal/mymodule/stuff/AddFavorite",
data: {
__RequestVerificationToken: token,
id: $(this).data("id")
},
success: function (response) {
alert("it worked");
}
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With