How to delete with ajax and ASP.NET MVC? - c#

I am trying to use ajax to send a delete request to ASP.NET MVC 5 framework.
There is a single page with a single red button.
In the CustomersController :
[HttpDelete]
[Route("^/customers/delete/{id:int}")]
public ActionResult Delete(int id)
{
CarDealerContext ctx = new CarDealerContext();
// Delete customer with given id...
// If I get a get a breakpoint in here I will be a happy camper!
return View();
}
In the RouteConfig :
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
In the view I only have :
<input id="deleteBtn" class="btn btn-danger" type="button"
value="Delete" data-id="1"/>
This is the script i use when the deleteBtn is clicked :
// The script is loaded after Bootstrap and jQuery.
$("#deleteBtn").on("click", function (event) {
event.preventDefault();
var id = $("#deleteBtn").attr("data-id");
$.ajax({
url: "/customers/delete/" + id,
type: 'delete',
data: { id: id }
});
})
// The request sends http://localhost:61402/customers/delete/1
// and the response is 404...
All I want is to activate the Delete method in the CustomersController.
I have tried pretty much everything in the Route(regex) attribute.
The second I change the Method to [HttpGet] it works.
Would also appreciate a good studying source on the subject.

You need to enable these verbs in IIS (put, delete)
Iisexpress may need config file edit

Change your Action Method as follows:
[HttpGet]
public ActionResult Delete(int id)
{
//Your Code
}
Two points to note:
1) Instead of HTTPDelete, you should set an HTTPGet annotation because the request you're sending from your view is a GET request, not one that's conventionally understood as a delete operation type request.
2) This may not be required in your case but I thought I'd mention it anyhow. ASP.NET MVC prefers convention over configuration. You don't need to explicitly set that route. The default route in the RouteConfig file will point to your method.
This would be the default configuration in the RouteConfig file:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = request to this Action Method (if you don't have it, it's probably a good idea UrlParameter.Optional }
);

Related

urls like "home.asp" redirect in mvc

I need to rebuild a website (in old they use classic ASP but they want to make it now MVC 4) and they dont want to change the urls. for example if the search screen's url is blabla.com/search.asp they want to keep it like this. But mvc doesn't allow to make urls like "search.asp". I want to keep url like this but render search View. Am I need to do this all one by one or there is a dynamic way for it?
for example
requested url = "blabla.com/string variable"
if variable.Substring(variable.Length - 4) == ".asp";
return View("variable.Substring(0, (variable.Length - 4))")
Note: Syntax is all wrong, I know. I just tried to explain the condition..
Note2: They want this because of "SEO" things. they don't want to lose their ratings. any method that doesn't change anything for google, they will accept that method I guess.
You need two things.
Define a route for *.asp
Add an handler for *.asp
RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "DefaultAsp",
url: "{controller}/{action}.asp/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Handler (WebConfig)
(This one needs to be inserted inside /system.webServer/handlers
<add name="AspFileHandler" path="*.asp" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
Doing this you are also making all URL's built with MVC available with .asp, that means that if you have an anchor that calls another View, that view will have the normal mvc URL with .asp suffix.
Note
This is generic, you only need to add this line once.
Home/Index displayed are just the default Controller and Action.
Note 2
Forgot to explain the handler.
You need it because IIS thinks you are asking for an ASP file an it'll try to reach that page and return an error since it doesn't exist.
With this handler you are allowing your application to handle those pages itself.
MVC does allows you to write a Route containing an extension, and pointing it to a specific Controller:
In RouteConfig.cs:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Test", "test.asp", new {controller = "Test", action = "test" });
}
Then, in your TestController.cs:
public class TestController : Controller
{
public ActionResult Test()
{
var obj = new Foo();
//Do some processing
return View(obj);
}
}
In this way, you can access http://www.foo.com/test.asp without issues, and maintaining the .asp your client requires.

ASP.NET route issue

I have the following in my RouteConfig.cs file
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Home Controller has the following:
[Route("NewIncident")]
public ActionResult NewIncident()
{
var viewModel = new IncidentFormViewModel();
viewModel.NextPage = 2;
return View("NewIncident",viewModel);
}
[Route("EditIncident/{id?}")]
public ActionResult EditIncident(int? id)
{
return View("EditIncidentPage" + id);
}
[Route("SaveIncident")]
public ActionResult SaveIncident(IncidentFormViewModel incidentviewmodel)
{
return RedirectToAction("EditIncident/" + incidentviewmodel.NextPage);
}
From my index view, I can click a button that has the #Html.ActionLink as follows:
#Html.ActionLink("New Incident", "NewIncident", "Home", null, new { #class = "btn btn-primary" })
I access the site at http://sitename/ and it loads my index page. I can click the New Incident button and it loads http://sitename/NewIncident just fine, but when I click the Save button on the New Incident form, it calls the SaveIncident function just fine, but when it hits the return RedirectToAction("EditIncident/" + incidentviewmodel.NextPage); it sends me to http://sitename/Home/EditIncident/2 instead of just http://sitename/EditIncident/2.
Any idea why its adding the /Home in there?
The parts of the URL are http://sitename/Controller/Action/Parameter.
Your EditIncident ActionResult lives inside your Home Controller, and so you will therefore always see Homein front of EditIncident.
When you redirect to another Action, you can specify which controller to look for the ActionResult in with RedirectToAction("ActionName", "ControllerName"). If no Controller is specified, it assumes you want to use the same controller that you are currently in.
yes, Tot Zam are ok, and you need to create an action result method on your controller, this should be bind with a view
Found the issue.
Apparently I need to use Redirect(), not RedirectToAction() for my purposes.
Once I changed this, it rendered the correct URL without adding /Home to it

MVC weird routing behaviour

I have a weird problem with some routing in MVC. Here's my scenario:
The user gets presented to the front page, once visiting the site: testproject.com:57416/Home.
The user has a menu on the left side, with the menu item "Solutions".
The user presses the menu item "Solutions" and the menu item expands, showing 5 sub items.
The user presses the sub item "Demo", and redirects to testproject.com:57416/Solutions/SetLayout?layoutString=page1%7Csize15
As the param layoutString is defined, the grid (DevExpress) know how to filter the data to show.
The grid is presented to the user, and (s)he can now navigate around the grid, manipulating the layout.
The user now wants to see the clean Demo layout again, and (s)he presses the menu item "Solutions" -> sub item "Demo".
The action ActionResult SetLayout(string layoutString) is not hit in this case, but instead the action ActionResult Index() is hit, thereby not setting the layout as the layoutString is not sent in as a param
The url that the user is redirected to, looks like this: testproejct.com:57416/Solutions?undefined=undefined
This might be a bit tiresome to read through, but it's the best way possible for me to explain my scenario as I have never seen anything like this before in MVC. I truly have no idea what's going on, and why the action ActionResult SetLayout(string layoutString) is not hit the second time.
I suppose I should show some code so we can narrow down the possibilities.
_Layout.cshtml (has menu items)
<li class="has-submenu">
Solutions
<ul class="nav sub-menu-list">
<li>
#Html.ActionLink("Demos", "SetLayout", "Solutions", new { layoutString = "page1|size15" }, null)
</li>
</ul>
</li>
ActionResult SetLayout(string layoutString)
public ActionResult SetLayout(string layoutString)
{
Session["Layout"] = layoutString;
return RedirectToAction("Index", "Solutions");
}
ActionResult Index()
public ActionResult Index ()
{
using(var db = new DataBasecontext())
{
var list = db.Solutions.ToList();
return View(list);
}
}
EDIT
I've found out that this only happens when I'm inside the same controller. The controller holding the action ActionResult SetLayout(string layoutString) is the SolutionsController. If I'm coming from, let's say the AccountController everything works fine, but changing from SolutionsController to another action in the SolutionsController doesn't work.
Come to think of it, could have something to do with my map routes?
routes.MapRoute(
name: "ControllerIdActionId",
url: "{controller}/{id}/{action}/{id2}",
default : new { id2 = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
default : new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Solutions",
url: "{controller}/{id}/{action}",
default : new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Seems that the layoutString parameter has missing parameter value on its second firing, thus empty string value inserted into Session key (and Javascript function related to that Session treat the empty string as undefined).
public ActionResult SetLayout(String layoutString)
{
// Test if the string is null or empty before filling session key,
// if it is empty one, leave the session key as is
if (!String.IsNullOrEmpty(layoutString)) {
Session["Layout"] = layoutString;
}
return RedirectToAction("Index", "Solutions");
}
Perhaps this is not the best way to do, I just viewed from SetLayout method perspective to fill Session key with proper query string.
Pass 5th parameter as null in your actionlink as below:
#Html.ActionLink("Demos", "SetLayout", "Solutions", new { layoutString = "page1|size15" },null)
I have not tested it but may be it is the issue..
I managed to solve the problem on my own.
I'm not quite sure what the issue was, but changing the Html.ActionLink() into Html.RouteLink()'s I was able to define what MapRoute to use.
I solved it by using the following MapRoute
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
)
I could then use the Html.RouteLink like so:
Html.Routelink("Demos", //Display text
"Default", //The MapRoute to use
new { layoutString = "page1|size15|", //The param (layoutString)
controller = "Solutions", //The controller
action = "SetLayout" }) //The action
I made no changes to the action:
public ActionResult SetLayout(string layoutString)
{
Session["Layout"] = layoutString;
return RedirectToAction("Index", "Solutions");
}

How to use RouteConfig to append a page URL

I am struggling to get my code work, but I think I've read enough to suggest this is the correct way to approach this.
On my intranet, I'd like the user to type in a single word to search into a textbox, and check a checkbox. When the new page loads, I'd like the URL rewritting services of ASP.NET MVC to kick in and change a value from
mysite.com/?id=blah&isChecked=true
to
mysite.com/home/index/blah/true
My code isn't working in the sense of it gives no error, but doesn't do what I am explaining. So, I've removed the check box to just focus on the textbox.
My only route is
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{MyType}",
defaults: new { controller = "Home", action = "Index", MyType = UrlParameter.Optional }
);
My Controller
public ActionResult Index()
{
ViewBag.Message = "Modify this";
return View();
}
[HttpGet]
public ActionResult Index(string MyType)
{
ViewBag.Message = "..." + MyType;
return View();
}
and my View has
#using (Html.BeginForm("Index", "Home",FormMethod.Get))
{
<input name="MyType" /><br />
<input type="submit" />
}
#Html.ActionLink("Click me", "Index", new { #MyType = "Blah" }) //renders correctly
The problem is, it shows the querystring still in the address bar
mysite.com/?MyType=MySearchValue
instead of
mysite.com/Home/Index/MySearchValue
You can't do this purely with routing because the browser will always send form values as query string parameters when they are part of a GET request. Once the request has been sent to the server, the MVC framework can't do anything about the URL that was used.
This leaves you with only one real option (assuming you don't want to send a custom request using JavaScript), which is to explicitly redirect to the desired URL (meaning you will always have two requests when this form is submitted).
The simplest way of doing this is simply in the controller (rather, in a separate controller to ensure that there is no conflict in method signatures):
public class FormController : Controller
{
public ActionResult Index(string MyType)
{
return RedirectToAction("Index", "MyProperController", new { MyType });
}
}
If you direct your form to this controller action, MVC will then use the routing engine to generate the proper URL for the real action and redirect the browser accordingly.
You could do this from the same controller action but it would involve inspecting the request URL to check whether a query string was used or not and redirecting back to the same action, which is a little odd.

How do I make this ASP.NET MVC Route for a twitter-style URL work?

trying to map the following style of route: http://site.com/username in the same way that you can do http://www.twitter.com/user
My initial solution was to have these routes:
//site.com/rathboma - maps to user details for rathboma
routes.MapRoute("Users", "{id}", new { controller = "Users", action = "Details" });
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "oneday" } // Parameter defaults
);
And that was working fine, until I tried to do the following in my 'Links' controller:
public ActionResult Details(string id)
{
int newId;
if (!int.TryParse(id, out newId))
return RedirectToAction("Index");
WebLink results = Service.GetWebLink(newId, 5);
if (results == null)
return RedirectToAction("Index");
return View(results);
}
These RedirectToAction methods try and return the browser to http://site.com/Users (I do have a users controller) instead of directing to http://site.com/Links/index
Why is this is happening?
How should I be organizing my routes to make this work properly?
I'm happy sacrificing http://site.com/links and moving to http://site.com/links/index if I have to. But how would I enforce that?
Thanks to all for any help
EDIT:
I know what's causing this, it's trying to redirect to http://site.com/links (the index page), but links is being picked up as a username and redirecting to /users/details, when it can't find the user 'links' it tries to redirect to the UsersController Index action which maps to /users, and the cycle continues ('users' is not a user it can find so redirects infinately).
So I guess My sub-question is: how to I make mvc always use /links/index instead of just using /links for the index page?
Try adding this route before your Users route:
routes.MapRoute("Links",
"{controller}/{id}",
new { controller = "Links", action = "Details" });
This should then work for
http://mysite.com/Links/id
&
http://mysite.com/username
I believe changing RedirectToAction("Index"); to RedirectToAction("Index", "Links"); in your Links controller should solve the issue without having to change your routes.
The problem is you have two very greedy routes. What I'd do is to break apart the default route to less greedy routes like this :
routes.MapRoute("Links",
"Links/{id}",
new { controller = "Links", action = "Index" });
routes.MapRoute("Users",
"{id}",
new { controller = "Users", action = "Details" });
routes.MapRoute("Default",
"",
new { controller = "Home", action = "Index" });
Causing the urls to be like the following :
site.com/links/5 - hits the Links controller
site.com/name - hits the Users controller
site.com/ - hits the home controller

Categories