ActionLink with HttpMethod=DELETE does not work - c#

I try to implement DELETE method for my mvc-application.
There is my link placed in View:
#Ajax.ActionLink("Delete", "Delete", "Users", new { userId = user.Id }, new AjaxOptions { HttpMethod = "DELETE" })
There is my controller method:
[HttpDelete]
public ActionResult Delete(string userId)
{
//...
return View("Index");
}
When I click on Delete-link, I get a 404 error.
In Fiddler I see, that my request perfomed by GET method!
If I execute DELETE request with the same Headers from Fiddler, I get the expected result - my request is coming right into the Delete method.
How can I use #Ajax.ActionLink correctly?
P.S.: I want to use only built-in methods.

Are you sure all the Unobtrusive libraries are loaded? An #Ajax.ActionLink generates a standard anchor tag. If the JavaScript libraries aren't loaded to handle the click event, you'll get the GET request you see in Fiddler.
Check to see if the jquery.unobtrusive-ajax.js script is included in a bundle that is referenced from your layout page or that you're explicitly loading it on specific pages in a scripts region.

Try this:
#Ajax.ActionLink("Delete", "Delete", "Users",
new { userId = user.Id },
new AjaxOptions { HttpMethod = "POST" })
I am not sure why you used 'Delete' for the HTTPMethod. Post will send the Id for data you want removed to the server and call the 'Delete' ActionResult specified here #Ajax.ActionLink("Delete", "Delete", "Users",.

Related

Issue deleting ASP.NET Identity in MVC Web Application

So after looking around for guides and tutorials of how can I delete ASP Users, I found the following code to be pretty neat:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(string id)
{
if (ModelState.IsValid)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByIdAsync(id);
var logins = user.Logins;
var rolesForUser = await UserManager.GetRolesAsync(id);
using (var transaction = context.Database.BeginTransaction())
{
foreach (var login in logins.ToList())
{
await UserManager.RemoveLoginAsync(login.UserId, new UserLoginInfo(login.LoginProvider, login.ProviderKey));
}
if (rolesForUser.Count() > 0)
{
foreach (var item in rolesForUser.ToList())
{
// item should be the name of the role
var result = await UserManager.RemoveFromRoleAsync(user.Id, item);
}
}
await UserManager.DeleteAsync(user);
transaction.Commit();
}
return RedirectToAction("Index");
}
else
{
return View();
}
}
My view looks something like this:
<td>
#Html.ActionLink("Edit", "Edit", new { id = user.UserId }) |
#Html.ActionLink("Delete", "DeleteConfirmed", new { id = user.UserId })
</td>
After clicking "Delete" here, in theory, it should have called the DeleteConfirmed method from the controller called "ManageUsersController". However, it returns this error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /ManageUsers/DeleteConfirmed/29ad177f-0285-43d2-b065-109876f270b9
What might be going wrong here? Is there another way that I should write the method in the controller? Thank you in advance
This answer is based on the default codes that .NET scaffold for us.
You're generating the Delete link using an extension of ActionLink which needs link text as the first parameter and action name as the second one. Your DeleteConfirmed action is a POST method; you can't generate a link to POST, .NET sees GET methods for links. So:
<td>
#Html.ActionLink("Edit", "Edit", new { id = user.UserId }) |
#Html.ActionLink("Delete", "Delete", new { id = user.UserId })
</td>
Make sure you have another method called Delete which is a GET one, in your controller.
Add another attribute to DeleteConfirmed:
[ActionName("Delete")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(string id)
{ ... }
If you need to delete the user when you click the Delete, you should use a POST form including the user id and anti-forgery token as hidden inputs and a submit button instead of link. But it's a best practice to show the user what they're deleting. That's why .NET generated two related actions for delete; Delete (to review) and DeleteConfirmed (to actually delete).

Display Success message on the same page when submit

I'm using Html.Beginform in view page and get the parameters using FormCollection to the controller i want to return the Success message on the same ViewPage as a result.i'm using following code,
public string InsertDetails(FormCollection collection)
{
string result = "Record Inserted Successfully!";
return result;
}
It shows the success message on the new page.How can i resolve this? what i have to return to get the Success message on the same page?
Personally, I'd pop the result string into the ViewBag.
public ActionResult InsertDetails(FormCollection collection)
{
//DO LOGIC TO INSERT DETAILS
ViewBag.result = "Record Inserted Successfully!";
return View();
}
Then on the web page:
<p>#ViewBag.result</p>
I have following Options.
1. Use Ajax Begin Form with AjaxOptions like below
#using (Ajax.BeginForm("ActionName", "ControllerName", new { area = "AreaName" }, new
AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "alert('Success');" //This will execute once the Ajax call is finished.
}, null))
{
<input type="submit" name="nameSubmit" value="Submit" />
}
2. Use JQuery to Manually Setup the XHR Request
$.ajax({
url: "#Url.Action("ActionName", "ControllerName", new { area = "AreaName" });",
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({param : Value})
})
.done(function () { alert('Success');}) //This will execute when you request is completed.
.fail(function () { })
My Suggestions
There are following disadvantages while using the FormCollection
Point - 1
In case FormCollection is being used...It will be mandatory to Type Cast the Primitive Type Values un-necessarily because while getting the entry of specific Index of the System.Collections.Specialized.NameValueCollection, value being returned is of type String. This situation will not come in case of Strongly Typed View-Models.
Issue - 2
When you submit the form and goes to Post Action Method, and View-Model as Parameter exists in the Action method, you have the provision to send back the Posted Values to you View. Otherwise, write the code again to send back via TempData/ViewData/ViewBag
Point - 3
We have Data Annotations that can be implemented in View Model or Custom Validations.
ASP.Net MVC simplifies model validatons using Data Annotation. Data Annotations are attributes thyat are applied over properties. We can create custom validation Attribute by inheriting the built-in Validation Attribute class.
Point - 4
Example you have the following HTML
<input type="text" name="textBox1" value="harsha" customAttr1 = "MyValue" />
Question : How can we access the value of customAttr1 from the above eg from inside the controller
Answer : When a form get posted only the name and value of elements are posted back to the server. You can also use Hidden Fields to post the Attributes to Post Action method.
Alternatives : Use a bit of jQuery to get the custom attribute values, and post that along with the form values to action method
Another option is to rather put what you got in your custom attributes in hidden controls
That's the reason, I would always prefer to use View-Models
we can do it on Form inside view
#using (Ajax.BeginForm("Action", "Controller", new AjaxOptions { HttpMethod = "POST", OnSuccess = "Showmessage" }))
[HttpPost]
public ActionResult Test(TestViewModel model)
{
return Json(new {isok=true, message="Your Message" });
}
function Showmessage(data)
{
$('#Element').html('Successfully Submitted');
}

Get query string from an ajax call

Hi I am trying to get a querystring from an ajax call and it does not seem to work so well.Here is my code:
#Ajax.ActionLink("Add To Cart" ,
"AddToCart" ,
"Products",
new {
ProductId = #products.ElementAt(0).Value
},
new AjaxOptions{
Url = "/Products/AddToCart",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "UpdateCart",
HttpMethod = "GET"
})
Each link I have in my application calls something like this:
Products/AddToCart?ProductId=5
This is the controller it calls:
public ActionResult AddToCart(string ProductId)
{
string ProductCeva = ProductId;
}
Now from what I learned so far about MVC3 I assumed that the parameter ProductId would be 5 in our case , but when I debug the code , I get that it is null.
What am I doing wrong here and how can I get the ProductId query string in this casE?
Remove the Url = "/Products/AddToCart", bit from your AjaxOptions.
Why?
Here's why. The following code:
#Ajax.ActionLink(
"Add To Cart" ,
"AddToCart" ,
"Products",
new {
ProductId = #products.ElementAt(0).Value
},
new AjaxOptions {
Url = "/Products/AddToCart",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "UpdateCart",
HttpMethod = "GET"
}
)
generates:
<a data-ajax="true" data-ajax-method="GET" data-ajax-mode="replace" data-ajax-update="#UpdateCart" data-ajax-url="/Products/AddToCart" href="/Products/AddToCart?ProductId=5">Add To Cart</a>
Now even if the href of the generated anchor is correct (/Products/AddToCart?ProductId=5) that's not what is used for the AJAX request. The jquery.unobtrusive-ajax.js that you are using and which unobtrusively AJAXifies all anchors uses the data-ajax-url attribute (if present) when sending the AJAX request instead of the href attribute. Now look at the value of the data-ajax-url attribute and you will understand why you get null in your controller action.
You would also have seen this if you had used FireBug or a similar javascript debugging tool because when you would have inspected the Network tab to see why your AJAX request is not working you would have seen the wrong url being used.
Long story short two things to remember from this question (the first being more important as it allows you to deduce the second):
Use FireBug
the Url property of the AjaxOptions allows you to override the url to be used when sending the AJAX request.

How to get [authorize] to work how I want?

I have this link
#Ajax.ActionLink("comment", "CreateDebateComment", new { id = Model.DebateID}, new AjaxOptions
{
UpdateTargetId = "comment-entry-box",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET"
});
Which triggers this controller
[Authorize]
public PartialViewResult CreateDebateComment(int id)
{
DebateIDForComment = id;
return PartialView("_CreateDebateCommentPartial");
}
If the user is not logged in they get redirected to the LogOn page but it loads in the comment-entry-box div instead of redirecting to the Login Page
I also tried this variation
public PartialViewResult CreateDebateComment(int id)
{
if (!User.Identity.IsAuthenticated)
{
RedirectToAction("LogOn", "Account");
}
DebateIDForComment = id;
return PartialView("_CreateDebateCommentPartial");
}
But it does not redirect and will still load the partialView
Does anyone know how I can get this to function as I want ? I need the Log On page to load as normal and not in the comment-entry-box.
You may take a look at the following blog post in which Phil Haack explains how you could suppress the forms authentication module from redirecting to the LogOn page if the the request was an AJAX request and return a 401 HTTP status code which could be intercepted by the client and redirect accordingly.
So either add the HttpModule he showed or install his NuGet package to your project and then all you have to do is to register a global ajax event that will be trigerred every time some of your AJAX requests on the page hits a 401 from the server:
<script type="text/javascript">
$.ajaxSetup({
statusCode: {
401: function () {
window.location.href = '#Url.Action("logon", "account")';
}
}
});
</script>
What if instead of using [Authorize] (which I realize IS the question)
You check authorization in code (eg !User.Identity.IsAuthenticated) and you wrap your response in json, a false on the client side redirects to the login [via javascript]
True is followed by the data you want to display
ie
{ "Success": "false" }
or
{ "Success": "true", "data": "blah blah blah" }

Internet Explorer displaying cache data over Ajax Pull with 304 Result

I have a tool that works perfectly fine in Chrome and FF. But with any versions of IE the browser is displaying cache info over doing an Ajax pull to retrieve the data.
Here's my setup:
I have criterias that I loop through:
#foreach (var item in Model)
{
<div class="sizeTDCriteria">
#Html.DisplayFor(modelItem => item.Text)
</div>
<div class="sizeTDCriteriaAction">
#Ajax.ImageActionLink(Url.Content("~/Content/images/icons/edit.png"), "Edit Criteria", "AddOrEditCriteria", "Management", new { competencySectionId = ViewBag.competencySectionId, criteriaId = item.Id }, new AjaxOptions { UpdateTargetId = "AddOrEditCriteriaFormContainer" }, new { #class = "iconPosition" })
#Ajax.ImageActionLink(Url.Content("~/Content/images/icons/delete.png"), "Delete Criteria", "RemoveCriteria", "Management", new { criteriaId = item.Id }, new AjaxOptions { UpdateTargetId = "CompetenciesManagementWrapper" }, new { #class = "iconPosition" })
</div>
}
The ImageActionLink is just a helper that creates a ActionLink with an image inside, I've tried doing this with a normal ActionLink and the issue occurs as well so you can ignore that. I've also tried to change the whole ImageActionLink by a plain <img> with a jQuery trigger with no difference.
What happens is that when a user clicks on the Edit link it will do an ajax call to the "AddOrEditCriteria", that ActionResult, finds the criteria, and displays the PartialView form back into the div "#AddOrEditCriteriaFormContainer".
So far so good, this works fine in all browsers.
But when I click a second time on that edit, instead of doing the ajax call, IE simply displays the PartialView from what it had in the cache, when all other browsers correctly pull the data again (which is required, as that view allows to edit the criterias, it could have been edited by someone else in the mean time).
The weird part is that IE is making the call but somehow it never reaches the server, it just uses the cache by using a Result 304. You can see from this Network capture:
URL Method Result Type Received Taken Initiator Wait?? Start?? Request?? Response?? Cache read?? Gap??
/PerformanceMVC/Management/AddOrEditCriteria?competencySectionId=178&criteriaId=369&X-Requested-With=XMLHttpRequest GET 304 text/html 182 B < 1 ms JS Library XMLHttpRequest
/PerformanceMVC/Management/AddOrEditCriteria?competencySectionId=178&criteriaId=369&X-Requested-With=XMLHttpRequest GET 304 text/html 182 B < 1 ms JS Library XMLHttpRequest
/PerformanceMVC/Management/AddOrEditCriteria?competencySectionId=178&criteriaId=369&X-Requested-With=XMLHttpRequest GET 200 text/html 1.53 KB 1.24 s JS Library XMLHttpRequest
The last one is the first to happen, the first two were done after and are getting a 304 return.
I found a way to fix it by adding a "breakcache" parameter to the ajax call with a random number, but that just doesn't seem like a good solution.
This issue has been screwing our users because they see data that should is not updated all because of IE.
IE will cache AJAX GETs unless you tell it not to.
You tell it not to via the Cache-Control header.
So here's what we do:
[CacheControl(HttpCacheability.NoCache), HttpGet]
public ActionResult MyAction() { return Json("Hi!", JsonRequestBehavior.AllowGet); }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class CacheControlAttribute : ActionFilterAttribute
{
public CacheControlAttribute(HttpCacheability cacheability)
{
this._cacheability = cacheability;
}
public HttpCacheability Cacheability { get { return this._cacheability; } }
private HttpCacheability _cacheability;
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
cache.SetCacheability(_cacheability);
}
}

Categories

Resources