Multiple actions matching the request - asp.net-web-api

I have the following 2 routes defined in WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "DefaultApiWithAction",
routeTemplate: "api/{controller}/{action}/{id}"
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And on the server these two methods:
public IEnumerable<SampleForm> Get()
{
// do stuff
}
public IEnumerable<SampleForm> GetSampleFormsByState([FromUri]string[] state)
{
// do stuff
}
I am getting the error 'Multiple actions were found that match the request' and pointing out these two methods. The request is like this:
http://localhost/tracker/api/sampleform?state[]=pending&state[]=rejected&state[]=removed
So here's the interesting thing... if I change the second method to this:
public IEnumerable<SampleForm> GetSampleFormsByState(string state)
{
// do stuff
}
and make a request like:
http://localhost/tracker/api/sampleform?state=pending
There is no problem.
What is going on? What is it about the array that causes my routing to fail? What do I need to do?

string[] is a complex type. That is the very reason you had to use [FromUri] to get the binding to work. So, both the following methods can be selected by Web API to handle HTTP GET with complex type parameter binding.
public IEnumerable<SampleForm> Get() { }
public IEnumerable<SampleForm> GetSampleFormsByState([FromUri]string[] state) { }
Once you change one of the signatures to string, which is a simple type, you no longer have two GET methods for complex types and web API is able to select the action method without any confusion.
To get around this problem, you can define just one action method to handle GET like this.
public IEnumerable<SampleForm> Get([FromUri]string[] state) { }
In the action method, just check for state != null && state.Length > 0 to use the state data from the request in the filter.

Related

Web Api route configNot Working

I have these 2 configs:
config.Routes.MapHttpRoute(
name: "CustomRate1",
routeTemplate: "api/CustomRate/{action}/{idCustomRate}",
defaults: new { controller = "CustomRate" }
);
config.Routes.MapHttpRoute(
name: "CustomRate",
routeTemplate: "api/CustomRate/{action}/{idCountry}",
defaults: new { controller = "CustomRate" }
);
However, only one of them works at a time. If I put CustomRate before the route works, but CustomRate1 does not, the reverse also happens, if I put CustomRate1 on top CustomRate does not work. Any suggestions
Currently in my controller I have
[HttpPost()]
[HttpOptions]
public CustomRateListReturnType GetCustomRateListByCountry(long idCountry)
{
}
[HttpPost()]
[HttpOptions]
public BaseReturnType DesactivateCustomRate(long idCustomRate)
{
}
I am currently using these two calls
http://127.0.0.1/quotesystemserver/api/CustomRate/GetCustomRateListByCountry/5
and
http://127.0.0.1/quotesystemserver/api/CustomRate/DesactivateCustomRate/3
Ok What I did is as follows:-
My config is now as follows:-
config.Routes.MapHttpRoute(
name: "CustomRate1",
routeTemplate: "api/CustomRate/{action}/{id}",
defaults: new { controller = "CustomRate" }
);
My controller contains:-
[HttpPost()]
[HttpOptions]
public CustomRateReturnType GetCustomRateListByCountry(long id)
{
}
[HttpPost()]
[HttpOptions]
public BaseReturnType DesactivateCustomRate(long id)
{
}
Just have one route.
The placeholders {action} and {id} will take the values what you pass in the URL.
If you call http://127.0.0.1/quotesystemserver/api/CustomRate/GetCustomRateListByCountry/5
,the route will take action as GetCustomRateListByCountry and id as 5.
Whatever you give the name to the placeholder { } doesnot matter. It is just the placeholder name. The value comes from the URL.
use attribute routing, it gives more clearer picture of the custom routes for any person who is developing the application for rest you can use config routes:
Decorate your controller with
[RoutePrefix("api/CustomRate")]
And your action methods as follows
HttpPost()]
[HttpOptions]
[Route("GetCustomRateListByCountry/{idCountry:long}]
public CustomRateListReturnType GetCustomRateListByCountry(long? idCountry)
{
}
[HttpPost()]
[HttpOptions]
[Route("DesactivateCustomRate/{idCustomRate:long}]
public BaseReturnType DesactivateCustomRate(long? idCustomRate)
{
}
Add the below code in webapiconfig.cs
config.MapHttpAttributeRoutes();

Overriding UseBufferedInputStream in Web Api when Attribute Routing is in use

I want to override the buffered input stream for a particular end point which is responsible for upload the files.
I've been following the advice in the great article here:
http://www.strathweb.com/2012/09/dealing-with-large-files-in-asp-net-web-api/
And this approach works whenever a templating approach to routing is taken. Like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
However, whenever I use attribute based routing,
config.MapHttpAttributeRoutes();
Then the RouteData is never populated:
public override bool UseBufferedInputStream(object hostContext)
{
var context = hostContext as HttpContextBase;
if (context != null && context.Request.RequestContext.RouteData.Values.Count>0)
{
if (string.Equals(context.Request.RequestContext.RouteData.Values["controller"].ToString(), "values", StringComparison.InvariantCultureIgnoreCase))
return false;
}
return true;
}
Is there another approach I can use, a different way of checking the route data for the request, in order to determine if I want the input buffered?

Can I have non Rest styled Methods in web api?

My question specifically is if i can support something like this
public class SomeTestController : ApiController
{
[System.Web.Http.AcceptVerbs("GET")]
public SomeInfo GetSomeBlah(int a, int b)
{
return new SomeInfo
{
Name = string.Format("GetSomeBlah - {0}", a),
CompanyID = b.ToString()
};
}
[System.Web.Http.AcceptVerbs("GET")]
public SomeInfo GetSomeBlah1(int a, int b)
{
return new SomeInfo
{
Name = string.Format("GetSomeBlah1 - {0}", a),
CompanyID = b.ToString()
};
}
}
I tried to add the following route
config.Routes.MapHttpRoute(
name: "DefaultApi4",
routeTemplate: "api/{controller}/{action}",
defaults: new {action = RouteParameter.Optional}
);
Edit: 4/7/15
I forgot to mention it should also work with rest styled methods.
Not sure why you are trying to do this, it sounds like you have misunderstood how routing works - Have you seen these tutorials on Routing and how ASP.net maps routes?
From point 3b below this means that if you name your input varibles more meaningfully, the framework will be able to map on the name and type coming in from the route as to which method to pick i.e. getSomeBlah or GetSomeBlack1
Summary for others,
1). Create a list of all actions on the controller that match the HTTP request method.
2). If the route dictionary has an "action" entry, remove actions whose name does not match this value.
3). Try to match action parameters to the URI, as follows:
a). For each action, get a list of the parameters that are a simple type, where the binding gets the parameter from the URI. Exclude optional parameters.
b). From this list, try to find a match for each parameter name, either in the route dictionary or in the URI query string. Matches are case insensitive and do not depend on the parameter order.
c). Select an action where every parameter in the list has a match in the URI.
d). If more that one action meets these criteria, pick the one with the most parameter matches.
4). Ignore actions with the [NonAction] attribute.
That will work if you remove the default route. I also indicated where the parameters are from.
Specify the input is from the URL:
public SomeInfo GetSomeBlah([FromUri] int a, [FromUri] int b)
Comment or remove this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Test URL:
http://localhost:64719/api/Values/GetSomeBlah?a=1&b=2
Response:
<ValuesController.SomeInfo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/SampleMVC.Controllers"> <CompanyID>2</CompanyID><Name>GetSomeBlah - 1</Name> </ValuesController.SomeInfo>
configure it like this:
config.Routes.MapHttpRoute("DefaultApi2",
"apiws/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute("DefaultApi",
"apiws/{controller}/{id}",
new { id = RouteParameter.Optional });
this works for both rest style and non-rest styles apis.
Note that the sequence of the two statements matters.
I managed to get it to work with the following code
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and Attribute routing.
[RoutePrefix("api/someother")]
public class SomeOtherController : ApiController
{
// GET: api/SomeOther
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET: api/SomeOther/5
public string Get(int id)
{
return "value";
}
// POST: api/SomeOther
public void Post([FromBody]string value)
{
}
// PUT: api/SomeOther/5
public void Put(int id, [FromBody]string value)
{
}
[System.Web.Http.HttpGet()]
[System.Web.Http.Route("Findsomething2")]
public CompanyInfo Findsomething2(int id, int b)
{
return new CompanyInfo
{
CompanyID = b.ToString(),
Name = id.ToString() + " 2"
};
}
// DELETE: api/SomeOther/5
public void Delete(int id)
{
}
}

Web API Help is not generated as expected

[RoutePrefix("Office")]
public class OfficeController : ApiController
{
[Route("~/Admin/{adid}")]
public string GetAdminById(int adid)
{
return string.Format("Enter value is {0}", adid);
}
[Route("~/User/{uid}")]
public string GetUserByName(int uid)
{
return string.Format("Enter value is {0}", uid);
}
}
but help is generated below
GET api/Office?adminid={adid}
GET api/Office?userid={uid}
but I am excepting
GET Office/Admin/adid
GET Office/User/uid
For this I added one more route, for admin at webapi.config
config.Routes.MapHttpRoute(
name: "OfficeadminApi",
routeTemplate: "api/{controller}/Admin/{adid}",
defaults: new { **adid** = RouteParameter.Optional }
);
then 2 signatures are generated by web api help are
GET api/Office?adid={adid}
GET api/Office/Admin/{adid} // I expected is generated but
what is the use of Attribute routing, if added no'f Routes at webapi.config?
as per Attribute routing it should generates, routes specified at attribute level.
Please clarify on this any one please ?

WebApi in MVC3, cannot hit my API Controller (500 internal error)

I have the below route mapped in my AreaRegistration:
public override void RegisterArea(AreaRegistrationContext context)
{
if (context != null)
{
context.Routes.MapHttpRoute(
name: "API_Default",
routeTemplate: "Areas/Test/AIO/api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
});
When I look at the Global.asax file, I can see the HttpRoute is being Registered, and is listed in the RouteTable.Routes as a {System.Web.Http.WebHost.Routing.HttpWebRoute}.
Problem is, when I go to the url... https://myRoot/Areas/Test/AIO/api/AioApi/test or https://myRoot/Areas/Test/AIO/api/AioApi, it's giving me a 500 internal server error.
I'm not sure how to view the actual error, when stepping thru the code I cannot see anything after it leaves Application_BeginRequest.
My controller code:
public class AioApiController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Any insight as to why I cannot hit the API controller? I can hit my regular MVC controller in the same context.
Any help is greatly appreciated!
One thing I found out with WebApi is that there are two Route collections: System.Web.Routing.RouteCollection and System.Web.Http.HttpRouteCollection. I believe (but can't remember) that you need to use the latter in order for your ApiController derivations to work properly (luckily the syntax is the same).

Resources