How to include the #Html.AntiForgeryToken() when deleting an object using a Delete link - asp.net-mvc-3

i have the following ajax.actionlink which calls a Delete action method for deleting an object:-
#if (!item.IsAlreadyAssigned(item.LabTestID))
{
string i = "Are You sure You want to delete (" + #item.Description.ToString() + ") ?";
#Ajax.ActionLink("Delete",
"Delete", "LabTest",
new { id = item.LabTestID },
new AjaxOptions
{ Confirm = i,
HttpMethod = "Post",
OnSuccess = "deletionconfirmation",
OnFailure = "deletionerror"
})
}
but is there a way to include #Html.AntiForgeryToken() with the Ajax.actionlink deletion call to make sure that no attacker can send a false deletion request?
BR

You need to use the Html.AntiForgeryToken helper which sets a cookie and emits a hidden field with the same value. When sending the AJAX request you need to add this value to the POST data as well.
So I would use a normal link instead of an Ajax link:
#Html.ActionLink(
"Delete",
"Delete",
"LabTest",
new {
id = item.LabTestID
},
new {
#class = "delete",
data_confirm = "Are You sure You want to delete (" + item.Description.ToString() + ") ?"
}
)
and then put the hidden field somewhere in the DOM (for example before the closing body tag):
#Html.AntiForgeryToken()
and finally unobtrusively AJAXify the delete anchor:
$(function () {
$('.delete').click(function () {
if (!confirm($(this).data('confirm'))) {
return false;
}
var token = $(':input:hidden[name*="RequestVerificationToken"]');
var data = { };
data[token.attr('name')] = token.val();
$.ajax({
url: this.href,
type: 'POST',
data: data,
success: function (result) {
},
error: function () {
}
});
return false;
});
});
Now you could decorate your Delete action with the ValidateAntiForgeryToken attribute:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id)
{
...
}

Modifying the answer by Bronx:
$.ajaxPrefilter(function (options, localOptions, jqXHR) {
var token, tokenQuery;
if (options.type.toLowerCase() !== 'get') {
token = GetAntiForgeryToken();
if (options.data.indexOf(token.name)===-1) {
tokenQuery = token.name + '=' + token.value;
options.data = options.data ? (options.data + '&' + tokenQuery)
: tokenQuery;
}
}
});
combined with this answer by Jon White
function GetAntiForgeryToken() {
var tokenField = $("input[type='hidden'][name$='RequestVerificationToken']");
if (tokenField.length == 0) { return null;
} else {
return {
name: tokenField[0].name,
value: tokenField[0].value
};
}
Edit
sorry - realised I am re-inventing the wheel here SO asp-net-mvc-antiforgerytoken-over-ajax/16495855#16495855

Related

ASP.net cascading dropdown list

trying to implement country state dropdown in mvc but couldn't..
conotroller :-
[HttpGet]
public ActionResult GetCities(int StateId)
{
Business.Services.City cityService = new Business.Services.City();
List<Business.Models.City> stateList = cityService.GetCityByStateId(StateId);
//var jsonSerialiser = new JavaScriptSerializer();
//var json = jsonSerialiser.Serialize(stateList);
return Json(new { stateList }, JsonRequestBehavior.AllowGet);
}
method:
public List<Models.City> GetCityByStateId(int StateId)
{
try
{
var list = new List<SelectListItem>();
Collection<DBParameters> parameters = new Collection<DBParameters>();
parameters.Add(new DBParameters() { Name = "StateId", DBType = DbType.Int32, Value = StateId });
var city = this.ExecuteProcedure<Models.City>("GetCityByState", parameters).ToList();
//if (city != null && city.Count > 0)
//{
// list = city.Select(x => new SelectListItem { Text = x.CityName, Value = x.StateId.ToString() }).ToList();
//}
return city;
}
catch (Exception ex)
{
throw;
}
}
change event:
$('.ddlstate').change(function () {
debugger;
$.ajax({
url: '#Url.Action("GetCities", "User")',
type: "GET",
data: { StateId: $(this).val() },
dataType: "json",
success: function (result) {
debugger;
//alert(result.stateList[0].CityId);
$.each(result.stateList, function () {
debugger;
$('.cityddl').append($("<option></option>").val(CityId).html(CityName));
});
},
error: function (result, status, jQxhr) {
alert("Error: " + result + "-" + status + "-" + jQxhr);
}
});
});
i get count of the citites in method and controller but when i run project and change state dropdown i got blank city dropdown. what is wrong?
It looks like you're missing a couple of things in the $.each() call.
You should pass the JSON result from the ajax call to the $.each
You also need to provide a parameter to the callback function so that the callback function has something to work with
It could look something like this:
$.each(result.stateList, function(index, city) {
$('.cityddl').append($("<option></option>").val(city.CityId).html(city.CityName));
});

Passing the Model back to Server when doing Ajax

here's the code in my .cshtml:
#model WebComposite.Models.MyModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.id)
#Html.TextBoxFor(m => m.name)
}
#Scripts.Render("~/Scripts/MyScript.js")
here's the code in MyScript.js:
$('form').submit(function (e)
{
e.preventDefault();
//normal Ajax (my own custom version as i'm sure every one has one of these:)
SimpleAjax("MyAction", function () { alert("Done"); });
});
And the Controller code:
[HttpPost]
public ActionResult MyAction(MyModel model)
{
//Problem here is model.id and model.name are empty
}
any ideas?
thanks in advance
After a quick look around I guess the solution is pretty darn simple (thank you jquery!!):
Simply serialize everything you have in the form and post it back to the controller from your ajax call:
$("form").serializeArray();
Here's my own full implementation of Ajax calls
There are 3 functions (you'll to call SimplAjaxPost(url, funcDelegate) and it will do the rest for you
function Ajax(url, dataObj, getOrPost, theContext, theContentType, theDataType, successFuncName, failedFuncName, alwaysFuncName)
{
//GET or POST:
if (getOrPost == null) { getOrPost = 'POST'; }
//Header (what we're sending to the server): http://stackoverflow.com/questions/2722750/ajax-datatype
if (theContentType == null) { theContentType = 'application/x-www-form-urlencoded; charset=UTF-8'; }
//response (what we're expeting in return):http://stackoverflow.com/questions/2722750/ajax-datatype
if (theDataType == null) { theDataType = ""; }
//exposing "this" to whatever: http://stackoverflow.com/questions/5097191/ajax-context-option
if (theContext == null) { theContext = document.body; }
var theURL = NoCache(url);
$.ajax(
{
url: theURL,
data: dataObj,
type: getOrPost,
contentType: theContentType,
dataType: theDataType,
context: theContext,
async: false,
success: successFuncName,
fail: failedFuncName,
always: alwaysFuncName,
complete: AjaxScripts
});
}
function SimpleAjax(url, successFunctionName)
{
var dataObj = null;
Ajax(url, dataObj, null, null, null, null, successFunctionName, null, null)
}
function SimpleAjaxPost(url, successFunctionName)
{
var dataObj = null;
if ($("form").length == 1)
{
dataObj = $("form").serializeArray();
}
Ajax(url, dataObj, null, null, null, null, successFunctionName, null, null)
}
function NoCache(url)
{
var t = new Date();
var qps = "?";
if (url.indexOf("?") > 0)
{
qps = "&";
}
return url + qps + t.getYear() + t.getMonth() + t.getDay() + t.getHours() + t.getMinutes() + t.getSeconds() + t.getMilliseconds();
}

Ajax post a JSON model to ASP.Net MVC4 with Anti-forgery token

I am submitting json model through ajax post. Its not working after adding user validation.
var token = $('input[name=""__RequestVerificationToken""]').val();
var headers = {};
headers['__RequestVerificationToken'] = token;
$.ajax({
url: '/SalesQuotation/Create',
cache: false,
headers: headers,
data: JSON.stringify(salesquotation),
type: 'POST',
contentType: 'application/json;',
dataType: 'json',
async: false,
success: function (result) {
if (result.Success == "1") {
window.location.href = "/SalesQuotation/Create";
}
else {
alert(result.ex);
}
}
});
Controller :
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Create(SalesQuotation salesquotation)
{
try
{
if (ModelState.IsValid)
{
if (salesquotation.QuotationId > 0)
{
var CurrentsalesQuotationSUb = db.SalesQuotationSubs.Where(p => p.QuotationId == salesquotation.QuotationId);
foreach (SalesQuotationSub ss in CurrentsalesQuotationSUb)
db.SalesQuotationSubs.Remove(ss);
var CurrentsalesQuotationDta = db.DTATrans.Where(p => p.QuotationId == salesquotation.QuotationId);
foreach (DTATran ss in CurrentsalesQuotationDta)
db.DTATrans.Remove(ss);
foreach (SalesQuotationSub ss in salesquotation.salesquotationsubs)
db.SalesQuotationSubs.Add(ss);
foreach (DTATran ss in salesquotation.dtatrans)
db.DTATrans.Add(ss);
db.Entry(salesquotation).State = EntityState.Modified;
}
else
{
db.SalesQuotations.Add(salesquotation);
}
db.SaveChanges();
}
}
catch (Exception ex)
{
return Json(new { Success = 0, ex = "Unable to save... " + ex.Message.ToString()});
}
return Json(new { Success = 1, ex = new Exception("Saved successfully.").Message.ToString() });
}
View:
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<input name="__RequestVerificationToken" type="hidden"
value="H4zpQFvPdmEdGCLsFgeByj0xg+BODBjIMvtSl5anoNaOfX4V69Pt1OvnjIbZuYrpgzWxWHIjbng==" />
The server return
What could be missing in my method. Please advice....
Attribute selectors should only have a single set of quotes around them. Your code has two quotes on each side.
This:
var token = $('input[name=""__RequestVerificationToken""]').val();
should be this:
var token = $('input[name="__RequestVerificationToken"]').val();
Use [ValidateJsonAntiForgeryToken] attribute in action method.

MVC3 and Twitter Bootstrap TypeAhead without plugin

I have gotten TypeAhead to work properly with static data and am able to call my controller function properly and get data but it is either A: Not parsing the data properly or B: The TypeAhead is not set up correctly.
JavaScript :
<input type="text" id="itemSearch" data-provide="typeahead" value="#ViewBag.Item" name="itemSearch"/>
$('#itemSearch').typeahead({
source: function (query, process) {
parts = [];
map = {};
$.ajax({
url: '#Url.Action("MakePartsArray")',
dataType: "json",
type: "POST",
data: {query: query},
success: function (data) {
$.each(data, function (i, part) {
map[part.description] = part;
parts.push(part.description);
});
typeahead.process(parts);
}
});
},
updater: function (item) {
selectedPart = map[item].itemNumber;
return item;
},
matcher: function (item) {
if (item.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1) {
return true;
}
},
sorter: function (items) {
return items.sort();
},
highlighter: function (item) {
var regex = new RegExp('(' + this.query + ')', 'gi');
return item.replace(regex, "<strong>$1</strong>");
}
});
Controller :
public ActionResult MakePartsArray(string query)
{
var possibleItem = query.ToLower();
var allItems = Db.PartInventorys.Where(l => l.ItemNumber.Contains(possibleItem)).Select(x => new { itemNumber = x.ItemNumber, description = x.Description });
return new JsonResult
{
ContentType = "text/plain",
Data = new { query, total = allItems.Count(), suggestions = allItems.Select(p => p.itemNumber).ToArray(), matches = allItems, },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
In my controller I see the data being retrieved correctly and it appears to parse properly but nothing is showing up for my TypeAhead.
Any idea on how to verify exactly where the breakdown is occurring or does anyone see direct fault in my code?
Problem was in the ajax call-
$.ajax({
url: '#Url.Action("MakePartsArray")',
dataType: "json",
type: "POST",
data: {query: query},
success: function (data) {
$.each(data.matches, function (i, part) {
var composedInfo = part.description + ' (' + part.itemNumber + ')';
map[composedInfo] = part;
parts.push(composedInfo);
});
process(parts);
}
});
and in the controller on the return type
return new JsonResult
{
ContentType = "application/json",
Data = new { query, total = allItems.Count(), suggestions = allItems.Select(p => p.itemNumber).ToArray(), matches = allItems },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};

Can't get jquery $ajax to call .NET MVC 2 controller method

I'm new to jquery and ajax - just can't seem to get this to work! See my related question: Use Json and AjaxLink to Toggle Link Values in ASP.NET MVC 2
Here's my jquery:
$(function () {
$("div[id^=add]").click(function (ev) {
ev.preventDefault();
updateMe(this.id.split('_')[1], "AddRequirement");
});
$("div[id^=remove]").click(function (ev) {
ev.preventDefault();
updateMe(this.id.split('_')[1], "RemoveRequirement");
});
});
function updateMe(myId, myAction) {
$.ajax({
type: "POST",
url: "AgreementType.aspx/" + myAction,
data: 'aId=' + <%:Model.AgreementType.Id%> + '&rId=' + myId,
dataType: "text",
error: function(request, msg){
alert( "Error upon saving request: " + msg );
},
success: function (data) {
alert(data);
}
});
}
Currently I have a two different divs. A foreach loop determines which one to display:
<%if(req.Agreements.Any(r => r.AgreementTypeId == Model.AgreementType.AgreementTypeId))
{%>
<div id="<%:string.Format("remove_{0}", req.Id)%>" class="divLink">Remove</div>
<% }
else
{ %>
<div id="<%:string.Format("add_{0}", req.Id)%>" class="divLink">Add</div>
<%{%>
Here's my controller action. Pretty simple:
public JsonResult AddRequirement(string aId, string rId)
{
string result = "Remove";
//Code to add requirement
return this.Json(result);
}
public JsonResult RemoveRequirement(string aID, string rID)
{
string result = "Add";
//Code to remove requirement
return this.Json(result);
}
}
All the success function needs to do it update the innerHtml of the target with the contents, and change the id to match. Seems so easy! And yet I can't seem to figure it out. TIA
Finally - the code that works. This will allow the user to click on a div which will call a different controller method based on the contents of that div, in effect allowing you to toggle toggle elements of the object in a foreach loop. I'm sure it could be improved upon; for instance, I probably don't need to get the value of the div from the controller method, but at least it works.
Javascript
<script type="text/javascript">
$(function () {
$("div[class^=toggleLink]").click(function (ev) {
ev.preventDefault();
var divText = $('#' + this.id).text();
if (divText == "Remove") {
updateMe(this.id, "Remove");
}
else if (divText == "Add") {
updateMe(this.id, "Add");
}
});
});
function updateMe(myId, myAction) {
$.ajax(
{
type: "POST",
url: "/AgreementType/" + myAction,
data: "aId=<%:Model.AgreementType.Id%>&rId=" + myId,
success: function (result) {
if (result.success) {
$('div#' + myId).text(result.value);
}
},
error: function (req, status, error) {
alert(req + " " + status + " " + error);
}
});
}
</script>
Controller
public ActionResult Add(string aId, string rId)
{
//Add to the template
string result = "Remove";
string nClass = "remLink";
return Json(new { success = true, value = result, newClass = nClass });
}
public ActionResult Remove(string aID, string rId)
{
//Remove from the template
string result = "Add";
string nClass = "addLink";
return Json(new { success = true, value = result, newClass = nClass });
}
Markup
<% foreach(var req in std.Requirements)%>
<% { %>
<tr>
<td>
<%if(req.Agreements.Any(r => r.AgreementTypeId == Model.AgreementType.Id))
{%>
<div id="<%:req.Id%>" class="toggleLink">Remove</div>
<% }
else { %>
<div id="<%:req.Id%>" class="toggleLink">Add</div>
<%} %>

Resources