MVC Web Api calling method not working properly - asp.net-mvc

I am trying to implement three methods controller
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
public IEnumerable<string> Get(int id)
{
return new string[] { "sa1", "sa2" };
}
[HttpGet]
public IEnumerable<string> password()
{
return new string[] { "password", "password" };
}
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But When i try to call http://localhost:49365/api/mycontrollername/password
it's always showing The request is invalid.

if you want to call an api function like "http://localhost:49365/api/mycontrollername/password"
you have to add ActionName attribute on the controller and add route on the Routeconfig.cs;
here is the example
[ActionName("GetEmployees")]
public IEnumerable<Employee> GETEmployees()
{
return _db.Employees.AsNoTracking();
}
routes.MapRoute(
name: "Default",
url: "api/{controller}/{action}/{id}",
defaults: new { controller = "Employees", action = "GetEmployees", id = UrlParameter.Optional }
);

I suspect it is attempting to call Get(int id) and then trying to pass the word "password" as the integer parameter. From what I can recall it's down to convention-over-configuration in that when you make a GET request it looks for a method named Get. In this case it finds one. Then based on the routing, i.e. after the controller name comes an ID, it looks at the next part of the URL, in this case "password", and then uses that as a value for the id argument.
If you were to remove the two Get methods you probably find that your URL works, but if you add other HttpGet methods you will run into other issues related to "multiple actions found". The following discussion may help if you decide you need to have multiple HttpGet methods:
Web Api Routing for multiple Get methods in ASP.NET MVC 4

Related

MVC Catch-All route is the only defined route, but it gives me 404

This is my Register method for routes in my web API project
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "AllRoutes",
routeTemplate: "{*url}",
defaults: new { controller = "IncomingRequest", action = "ProcessRequest" });
I would expect everything to go my ProcessRequest method on my IncomingRequest controller. However all routes result in 404. e.g.
http://localhost/CatCatcher/Cat/3
Can anyone advise what I might have missed?
Try this:
routes.MapRoute(
"AllRoutes",
"{*id}",
new { controller = "IncomingRequest", action = "ProcessRequest", id = "" }
);
Sorry, i've tested it now....
In web API project you cant set routes by different ways like:
config.Routes.MapHttpRoute(
name: "Routes",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And to achieve URL ("http://localhost/CatCatcher/Cat/3") you can set route prefix on controller and action also Here is example for that:
[RoutePrefix("CatCatcher")]
public class CatCatcherController : ApiController
{
[HttpGet]
[Route("Cat")]
public object Cat(int Id){
//Do something
}
}
And if you want to go on Execution before hitting Action then lets follow the example in which I secure API with IP/Domain here it is:
public class AuthorizeIPAddressAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
//Even you can do here what you want
//Get users IP Address
string ipAddress = HttpContext.Current.Request.UserHostAddress;
if (!IsIpAddressValid(ipAddress.Trim()))//check allow ip
{
//Send back a HTTP Status code of 403 Forbidden
filterContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.NotFound);
}
base.OnActionExecuting(filterContext);
}
After Adding this class in project you just have to do this on your controller:
[AuthorizeIPAddress]
[RoutePrefix("CatCatcher")]
public class CatCatcherController : ApiController
{
[HttpGet]
[Route("Cat")]
public object Cat(int Id){
//Do something
}
}
Hopefully this will help you..

Multiple actions were found that match the request?

I m new to WebAPI and just exploring its default sample "value" controller which is there out of box with project.
I see it was already having two Get methods:
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
I tried and changed int id with a complex type and received "Multiple actions were found that match the request"
Why is that it was working fine beofre ?
My route is defuatl:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I m passing a complex object in body using Get methoed, I know it is not Restful way but please help me understand it.
Much appreciated.
You can use ActionName annotation for this issue. For example use:
[ActionName("IEnumerableGet")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
So you can call IEnumerableGet to have this method get called.

Why does #Html.ActionLink not use RouteData

I have the simplest setup:
An empty asp.net MVC application with one controller:
public class HomeController : Controller
{
public ActionResult Edit(int id)
{
return View();
}
public ActionResult Commit(int id)
{
return View();
}
}
My Edit.cshtml has a call to ActionLink() like so:
#Html.ActionLink("Commit Data", "Commit")
If I now access the Edit-Action through "/Home/Edit/2" I would expect that the rendered link directs the user to "/Home/Commit/2".
It does not :( ... The link is created to "Home/Commit", completely disregarding the current RouteData entries.
I am using the default routing configuration (have not added any routes).
One way to fix this would be to add an explicit route for both actions:
routes.MapRoute(
name: null,
url: "Home/Edit/{id}",
defaults: new { controller = "Home", action = "Edit" }
);
routes.MapRoute(
name: null,
url: "Home/Commit/{id}",
defaults: new { controller = "Home", action = "Commit" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This works - but I really dont want to explicitly define every single route in the app - if I am using the "default" pattern...
The second solution would be to just add the routing-values manually like so:
#Html.ActionLink("Commit Data", "Commit", "Home", new {id = Model.Id})
But this also seems not right - ActionLink SHOULD use the current routing information, should it not?
What am I missing?
Ok, in case someone else is wondering the same thing - or just wants to have a solution that works...
I simply created my own #ActionLink() helper method on my custom ViewPage base class:
protected MvcHtmlString ActionLink(string linkText, string actionName)
{
var routeData = ViewContext.RequestContext.RouteData;
var id = routeData.Values["id"];
if (id != null)
return Html.ActionLink(linkText, actionName, new {id = id});
return Html.ActionLink(linkText, actionName);
}
This is exactly what I wanted. Now I can call
#ActionLink("Commit", "Commit")
and when I'm in the context of something with an id, the link will point to the appropriate route for this Id.

Overload WebAPI controller action doesn't work with viewmodels?

The following works:
public class UsageController : ApiController
{
public int GetMilk(string param1, string param2)
{
return -1;
}
public string GetBilling(string param1)
{
return null;
}
}
But the following throws a "Multiple actions were found that match the request" Exception?!?!
public class UsageController : ApiController
{
public int GetMilk(MilkVM vm)
{
return -1;
}
public string GetBilling(BillingVM vm)
{
return null;
}
}
How can I fix this?
By default, ASP.NET Web API selects an action method based on the HTTP verb and the action method parameters. In the second case, you have two action methods that can handle GET and they have one parameter each. When Web API tries to find an action method (more info here) for your GET, it will find both the methods.
You can follow RPC-style URI if you must have action methods like these. Add a mapping in WebApiConfig.cs like this.
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "RpcStyle",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Then, make GET to URIs with action methods like so: http://localhost:port/api/usage/getmilk?a=1&b=2.
BTW, your action methods will not be able to bind the query string to a complex type by default. Use FromUri attribute like this: public int GetMilk([FromUri]MilkVM vm). GET requests must not have a request body and hence complex types will not be bound by default.

How to bypass the exception multiple actions were found in ASP.NET Web API

When trying to find solution for this below question:
MVC Web Api Route with default action not working
Mostly I run across the problem "multiple actions were found". If routing mechanism finds out more than one actions matched with a route, it throws out the exception 500.
For example, there are two actions in ValuesController:
[HttpGet]
public IEnumerable<String> Active()
{
var result = new List<string> { "active1", "active2" };
return result;
}
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
which match with default route:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
With
GET: /api/values
will get error multiple actions were found.
My question: how I can bypass the exception "multiple actions were found" by choosing specified action (choose the first matched action is also okay).
Update 1: Update from tugberk's comment, thanks to point out.
Update 2: Update from Mike's comment, seems the question is not correct much, so I change the way to ask by not mentioning the routing constraint.
Thanks
First of all, it shouldn't be possible for those route to match Active action unless you apply HttpGet attribute on it. I will assume you already did that. The second thing is that what you want is not logical. You would like to make a get request and have two identical actions. You are now using so-called HTTP-method-based routing. For you situation, I am sure that so-called action-based routing is the best.
Assuming you have following two actions:
[HttpGet]
public IEnumerable<String> Active()
{
var result = new List<string> { "active1", "active2" };
return result;
}
[HttpGet]
public IEnumerable<string> Retrieve()
{
return new[] { "value1", "value2" };
}
You route should be as follows:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Then you can request to those with following URIs:
GET /controllername/retrieve
GET /controllername/active

Resources