WebApi call fails for other than the default route - asp.net-mvc-3

I have an Mvc3 project that includes both Mvc and Api controllers. When I run the application without specifying the "controller/action", the "Default" route is selected and the "home/index" page is rendered. When that page runs, an Ajax call is made using url: "api/controller", returning json, which is then used to populate a table on the page.
<script type="text/javascript">
$(function () {
var $products = $("#products");
$.ajax({
url: "api/products",
contentType: "json",
success: function (data) {
$.each(data, function (index, item) {
$products.append("<tr><td>" + item.ProductCode + "</td>" +
"<td>" + item.Description + "</td>");
});
}
});
});
</script>
However, when a request is made to the same page specifying the "controller/action", as in "localhost/home/index", the Ajax call is translated to "/home/api/controller" and of course the request cannot complete or return any results, since the ApiController cannot be found.
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Considering that it must be a routing issue, I proceeded to resolve this by adding a route:
routes.MapHttpRoute(
"HomeApi",
"home/api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
This was successful. As was my next change to:
routes.MapHttpRoute(
"AnyApi",
"{folder}/api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
Which also works but leaves me somewhat skeptical as to whether it is the correct way to handle combining WebApi with Mvc.
Is this the correct way to handle this? Or are there better alternatives?

Considering that it must be a routing issue, I proceeded to resolve this by adding a route:
No, that's not a routing issue at all. Your routes are perfectly fine:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Your issue stems from the fact that you have hardcoded the url in your javascript instead of using url helpers to generate it. You should absolutely never hardcode an url inside an ASP.NET MVC application. You should always use url helpers.
So:
<script type="text/javascript">
$(function () {
var $products = $("#products");
var url = '#Url.RouteUrl("DefaultApi", new { httproute = "", controller = "products" })';
$.ajax({
url: url,
success: function (data) {
$.each(data, function (index, item) {
$products.append("<tr><td>" + item.ProductCode + "</td>" +
"<td>" + item.Description + "</td>");
});
}
});
});
</script>
Also notice that I have removed the contentType: 'json' from your AJAX call because first the correct content type is contentType: 'application/json' and second in this case you are not sending any data in the request, so you shouldn't be setting it to application/json.

There's no need to add a route there -- you can specify the API URL so that it will always work:
/api/controller
In some cases you may need to use the Url.RouteUrl helper to create URLs explicitly based on the route (by route name and/or route values). For example, this would specify the route named "DefaultApi" with controller=products:
#Url.RouteUrl("DefaultApi", new { controller = "products" })

Related

Ajax calls stop working while using MVC Custom routes

I have defined custom routes for user friendly URLs.
But unable to call default route using ajax or on using Ajax.BeginForm() On removing custom routes, all works perfectly.
This is my RouteConfig.cs
//product view route
routes.MapRoute(
name: "ProductView",
url: "product/{product_name}/{id}",
new { controller = "Product", action = "View", product_name = UrlParameter.Optional, id = UrlParameter.Optional },
new[] { "NoveltyApp.Controllers" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "NoveltyApp.Controllers" }
);
Product View URL: http://localhost:56379/product/Camlin-Kokuyo-Acrylic-Color-Box---9ml-Tubes-1/1
ProductController.cs
public string ProductReviews()
{
return "Product review list";
}
Ajax call from Product view
<button type="button" class="btn btn-thm nf-pr-vi__btn" onclick="reviews()">Reviews</button>
<script>
function reviews() {
$.ajax({
url: '/Product/ProductReviews',
type: 'GET',
success: function (data) {
alert(data);
}
});
}
</script>
But on click button controller action not getting hit. Please help me out with a solution.

Url doesn't changing when redirecting to another action method in same controller

In myC# MVC project, i have a 'Home' controller like below,
Controller
public ActionResult Index()
{
return this.View();
}
public ActionResult LoadVideo()
{
return RedirectToAction("LoadVideoExt");
}
public ActionResult LoadVideoExt()
{
return this.View();
}
And my problem is, my url is like this http://localhost:51241/Home/Index when running the code. I have a button in my Index view to call LoadVideo from there I am redirecting to another action and load that page LoadVideoExt. The process is working fine but the final view is not rendering and also my url is not changing accordingly (as I am expecting http://localhost:51241/MyJob/LoadVideoExt). I am a bit confused here. Could someone help me out or point me in the right path?
Sample code attached below.
View (Index.cshtml)
function uploadMedia() {
linkUrl = '#Url.Action("LoadVideo", "Home", new { area = string.Empty })';
var formData = new FormData($('#fromIndex')[0]);
$.ajax({
url: linkUrl,
type: 'Post',
data: formData,
cache: false,
contentType: false,
processData: false,
beforeSend: function () { },
success: function (result) {
alert(0);
},
xhr: function () { // Custom XMLHttpRequest
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) { // Check if upload property exists
// Progress code if you want
}
return myXhr;
},
error: function () {
}
});
}
RouteConfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "DefaultMultipleParams",
url: "{controller}/{action}/{id}/{state}"
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
View (LoadVideoExt.cshtml)
#{
ViewBag.Title = "LoadVideoExt";
}
<h2>Gotcha!</h2>
Let assume my url is like this http://localhost:51241/MyJob/Index when running the code. I have a button in my 'Index' view to call LoadVideo from there I am redirecting to another action and load that page LoadVideoExt. The process is working fine but the final view is not rendering and also my url is not changing accordingly (as I am expecting http://localhost:51241/MyJob/LoadVideoExt). I am a bit confused here. Could someone help me out or point me in the right path?

Call web API string parameter

I have this function
[System.Web.Http.HttpGet]
[System.Web.Http.ActionName("StartProcess")]
public object StartProcess(string items)
{
//do stuff
}
trying to call with
$.ajax({
url: '/api/Details/StartProcess',
type: 'get',
contentType: 'application/json',
data: items,
success: function() {
logger.log('Successful', "", "", true);
},
error: function(error) {
var jsonValue = jQuery.parseJSON(error.responseText);
});
Keep getting 404 error. The rest of my calls work but this is the first one that I need to send a parameter.
items is just a comma delimited string.
this is my route info.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id=Urlameter.Optional }
);
Any ideas what I am missing?
you are getting 404 error because your routing table is not able to resolve the url "/api/Details/StartProcess"
In order to make the WebAPI routing work you need to modify the "MapHttpRoute()" function of route collection and not the "MapRoute()"
So please change the API routing as below (assuming you are using default api) and it should work fine.
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = RouteParameter.Optional, id = RouteParameter.Optional }
);
The parameter in Web API needs to follow the parameter in routing config.
In your example the easiest would be to change string items to string id.
Alternatly you could add api/{controller}/{action}/{items} to the route config.
Also, if you don't fancy changing your API route config (which would lead to minor complications with calling simple Get and Put methods) you can change your controller action annotation from
[System.Web.Http.ActionName("StartProcess")]
to
[Route("StartProcess/{items}")]
Also, you'll need to annotate your controller with:
[RoutePrefix("api/details")]

Ajax Post: 405 Method Not Allowed

Within my API Controller called Payment, I have the following method:
[HttpPost]
public HttpResponseMessage Charge(Payment payment)
{
var processedPayment = _paymentProcessor.Charge(payment);
var response = Request.CreateResponse(processedPayment.Status != "PAID" ? HttpStatusCode.ExpectationFailed : HttpStatusCode.OK, processedPayment);
return response;
}
In my HTML page I have:
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "http://localhost:65396/api/payment/charge",
data: $('#addPayment').serialize(),
dataType: "json",
success: function (data) {
alert(data);
}
});
Whenever I fire the POST, I get
"NetworkError: 405 Method Not Allowed - http://localhost:65396/api/payment/charge"
What am I missing?
Thank you.
UPDATE
Here's the routing information (default)
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Most likely your routing is not configured for the action to be invoked. Hence the request ends up in nowhere and ASP.NET Web API sends a blank-out message "method not allowed".
Can you please update the question with your routing?
UPDATE
As I thought! You are sending to http://localhost:65396/api/payment/charge while you need to send to http://localhost:65396/api/payment - assuming your controller is called PaymentController.
Note that route does not have action.
Turns out I needed to implement CORS support. http://blogs.msdn.com/b/carlosfigueira/archive/2012/02/20/implementing-cors-support-in-asp-net-web-apis.aspx
I had the same problem with my controller.
The only thing which is different is the ending of the URL.
Add "/" to "http://localhost:65396/api/payment/charge" at the end, that helped me

Do i have to list all controller functions?

Do i have to list all controller functions in Global.asax.cs file?
Im creating a api for my workout and have created the controller WorkoutController. It has the action AddWorkout that takes some parameters, for instance username, password, duration and type. Two first are string, two last is ints.
Now, do i need to create a route for it? And every action with different action signature? Why could it not fall in under the default route? Calling it would break if i dont supply the correct variables, but i know what im doing :D
routes.MapRoute(
"AddWorkout", // Route name
"Workout/AddWorkout/", // URL with parameters
new { controller = "Workout", action = "AddWorkout" } // Parameter defaults
);
??? :D ???
You can easily create a REST Api:
routes.MapRoute(
"Workout", // Route name
"{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
and use for example of your Workout:
public class WorkoutController : Controller
{
public ActionResult Index()
{
return RedirectToAction("Index", "Help");
}
[HttpPost]
public ActionResult Workout(FormCollection form)
{
// HTTP POST: ADD Workout
// process form and return JSON
return Json(myObject);
}
[HttpDelete]
public ActionResult Workout(string id)
{
// HTTP DELETE: REMOVE Workout
// process form and return JSON
return Json(myObject);
}
[HttpGet]
public ActionResult Workout(string id)
{
// HTTP GET: GET Workout
// process form and return JSON
return Json(myObject);
}
}
But I would suggest you to use WCF though :)
From a client side:
$.ajax({
type: "POST",
url: "/Workout/Workout",
data: {
'type': '123456',
'height': '171'
}
success: function(msg){
alert( "Data Saved: " + msg );
}
});
$.ajax({
type: "DELETE",
url: "/Workout/Workout",
data: { 'id': '123456' }
success: function(msg){
alert( "Data Saved: " + msg );
}
});
$.get("/Workout/Workout", { 'id': '123456' }, function(msg){
alert( "Data Saved: " + msg );
});
remember to create a Login method that you would send a token that is required in all actions, so you know that the user manipulating your data is real.
Read this.
The "Url" property on the Route class defines the Url matching rule
that should be used to evaluate if a route rule applies to a
particular incoming request.
No, you don't have to do this at all. The default route looks at what methods are defined and will figure out what to call as long as your parameters line up to some method on the controller.

Resources