Multiple actions were found that match the request:*** - c#

I am trying to call WEBAPI from postman .My AI method is post but when i execute it i get below given error
Multiple actions were found that match the request:***
Below is my Code:
webapi route.config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "Api Default",
routeTemplate: "api/{controller}/{method}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
}
}
APIController:
public class MembershipController : ApiController
{
[System.Web.Http.ActionName("Upload")]
public HttpResponseMessage Upload(string employeedetails)
{
`Some Code`
}
[System.Web.Http.ActionName("BulkUpload")]
[System.Web.Http.HttpPost]
public HttpResponseMessage BulkUpload(string employeedetails)
{
`Some Code`
}
[System.Web.Http.ActionName("Transfer")]
public HttpResponseMessage Transfer(string employeedetails)
{
`Some Code`
}
}
I am not getting whats going wrong method has different action name and route config is also fully qualified api url which contain controller method and id as optional parameter.To identify url this should be sufficient but it's not working.
Am i missing anything?

If this is RESTful API, you cannot have three HttpPost, unless you differentiate them by the URL slugs.
Easiest way is to use attribute route. E.g.
public class MembershipController : ApiController
{
// POST api/Membership/data/Upload
[Route("api/Membership/{employeedetails}/Upload")]
[HttpPost]
public HttpResponseMessage Upload(string employeedetails)
{
`Some Code`
}
// POST api/Membership/data/Bulk
[Route("api/Membership/{employeedetails}/Bulk")]
[HttpPost]
public HttpResponseMessage BulkUpload(string employeedetails)
{
`Some Code`
}
// POST api/Membership/data/Transfer
[Route("api/Membership/{employeedetails}/Transfer")]
[HttpPost]
public HttpResponseMessage Transfer(string employeedetails)
{
`Some Code`
}
}

Solution 1:
I have added Route Config in WebApiConfig class
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "Membership",
routeTemplate: "api/{controller}/{method}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "Api Default",
routeTemplate: "api/{controller}/{method}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
config.EnableQuerySupport();
// To disable tracing in your application, please comment out or remove the following line of code
// For more information, refer to: http://www.asp.net/web-api
config.EnableSystemDiagnosticsTracing();
}
}
Solution 2
public class MembershipController : ApiController
{
[System.Web.Http.ActionName("Upload")]
public HttpResponseMessage Upload([FromBody]string employeedetails)
{
`Some Code`
}
[System.Web.Http.HttpRoute("api/membeship/BulkUpload")]
[System.Web.Http.HttpPost]
public HttpResponseMessage BulkUpload(string employeedetails)
{
`Some Code`
}
[System.Web.Http.HttpRoute("api/membeship/Transfer")]
public HttpResponseMessage Transfer([FromBody]string employeedetails)
{
`Some Code`
}
}
If we compare Solution 1 and solution 2 then Solution 1 will work but it need a query string parameter where as sollution 2 will also work for post parameters(FormBody)
I am looking in details what does solution 2 makes a difference. Because when we remove HTTPRoute From solution 2 then it's also need query string parameter only and if we try to pass parameter using post then it get passed as null value. Very soon i will find it out and will share detail analysis on stack overflow.
Happy Coding

Related

WebApi ActionName delete route not working

my project contains several WebApi controllers and each of them provides usually three actions: get(guid), post(data) and delete(guid),
A default route is described in the WebApiconfig for this requirement. (name: ControllerAndId)
Now I have to implement a controller which has to handle different post actions. For this requirement I tried to map another route with ActionNames. (name: ControllerAndActionAndId)
Since I have mapped the ControllerAndActionAndId route it is not possible to call the delete route of the "normal" controller (example: Contactscontroller). All routes are working except the delete routes.
StatusCode: 404, ReasonPhrase: 'Not Found'
There is an example of an usually ApiController:
public class ContactsController : ApiController
{
public IEnumerable<Contact> Get()
{
return GetContacts();
}
public HttpResponseMessage Post(Contact contact)
{
SaveContact(contact);
return Request.CreateResponse<Guid>(_code, contact.Id);
}
public void Delete(Guid id)
{
DeleteContact(id);
}
}
Controller with ActionName-Route:
public class AttachmentsController : ApiController
{
[HttpGet]
public Attachment Get(Guid attachmentId)
{
return GetAttachment(attachmentId);
}
[HttpPost]
[ActionName("save")]
public HttpResponseMessage Save(AttachmentSaveData saveData)
{
SaveAttachment(saveData);
}
[HttpPost]
[ActionName("remove")]
public HttpResponseMessage Remove(AttachmentDeleteData deleteData)
{
DeleteAttachment(deleteData);
}
}
WebApiConfig:
// Web API routes
config.MapHttpAttributeRoutes();
// Controller with ID
// To handle routes like `/api/VTRouting/route/1`
config.Routes.MapHttpRoute(
name: "ControllerAndActionAndId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new
{
id = RouteParameter.Optional,
action = RouteParameter.Optional
}
);
// Controller with ID
// To handle routes like `/api/VTRouting/1`
config.Routes.MapHttpRoute(
name: "ControllerAndId",
routeTemplate: "api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);
ClientAction delete function:
private void Delete(string uri, int id)
{
using (HttpClient _client = new HttpClient())
{
_client.BaseAddress = BaseAddress;
string _url = string.Format("{0}/{1}", uri, id);
var _response = _client.DeleteAsync(_url).Result;
if (!_response.IsSuccessStatusCode)
{
throw new Exception();
}
}
}
I currently have no further idea how to solve this problem.
If you use Web API, you need add HTTP verb to action.
For example, your code must be as below:
public class ContactsController : ApiController
{
[HttpGet]
public IEnumerable<Contact> Get()
{
return GetContacts();
}
[HttpPost]
public HttpResponseMessage Post(Contact contact)
{
SaveContact(contact);
return Request.CreateResponse<Guid>(_code, contact.Id);
}
[HttpDelete]
public void Delete(Guid id)
{
DeleteContact(id);
}
}
Pay attention to Delete action.
If you use HttpDelete verb on action, you must send delete request from your client httpClient.DeleteAsync(...).
If you use HttpPost verb on action, you must send post request from your client httpClient.PostAsync(...).
AttachmentsController is similar to ContactsController.
I was focused to much on actions and routes of the controller.
But the solution was easy to find at client side:
private void Delete<T>(string uri, T value)
{
using (HttpClient _client = new HttpClient())
{
_client.BaseAddress = BaseAddress;
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string _url = string.Format("{0}/{1}", uri, value);
var _response = _client.DeleteAsync(_url).Result;
}
}
This solution requires only one route in WebApiConfig:
config.Routes.MapHttpRoute(
name: "ControllerAndId",
routeTemplate: "api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);
Soo easy.. thanks a lot !

Web API Attribute routing in Sitecore 8.1 not working

As Atrribute routing does not work in sitecore 8.1 out of the box, I am following https://github.com/Krusen/Sitecore.WebApi
And got the uget package for Krusen.Sitecore.WebApi.Custom.
This is my ConfigureWebApi class
public class ConfigureWebApi
{
public void Process(PipelineArgs args)
{
GlobalConfiguration.Configure(config => config.Routes.MapHttpRoute(
name: "myApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
));
GlobalConfiguration.Configure(config => config.MapHttpAttributeRoutes());
GlobalConfiguration.Configure(ReplaceControllerSelector);
}
private static void ReplaceControllerSelector(HttpConfiguration config)
{
config.Services.Replace(typeof (IHttpControllerSelector),
new CustomHttpControllerSelector(config, new NamespaceQualifiedUniqueNameGenerator()));
}
}
And this is my controller
[RoutePrefix("windows")]
public class WmsController : ApiController
{
[HttpGet]
[Route("hi")]
public IHttpActionResult Hello()
{
return Ok("Welcome to my Api.");
}
}
When I call this:
http://my.api.local/api/wms/hello
works.
But when I call
http://my.api.local/api/windows/hi
does not work. It says 404.
Am I missing something !!
The second call is not working because Attribute routing must be configured before Convention-based routes to avoid route conflicts.
public void Process(PipelineArgs args) {
GlobalConfiguration.Configure(config => {
// Map Attribute Routes
config.MapHttpAttributeRoutes();
// Map Convention-based Routes
config.Routes.MapHttpRoute(
name: "myApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Replace IHttpControllerSelector with our custom implementation
ReplaceControllerSelector(config);
});
}
Which I also believe is how it was shown in the documentation in the linked repo
Secondly based on the RoutePrefix("window") and Route("hi") in the ApiController the mapped attribute route would be mapped as
http://my.api.local/windows/hi
To get http://my.api.local/api/windows/hi to map to the desired action you would need to update the route prefix as already explained in one of the other answers.
You need to add "api/" into your controller attribute routing
[RoutePrefix("api/windows")]
public class WmsController : ApiController
{
[HttpGet]
[Route("hi")]
public IHttpActionResult Hello()
{
return Ok("Welcome to my Api.");
}
}

WebApi hard coded controller routing

I am trying to write a self hosted WebAPI server. I want all routes to go to a single controller. This controller can pick out the controller part of the url and use this to decide an appropriate response.
I have the following route configuration:
_configuration.Routes.MapHttpRoute
(
name: "DefaultApi",
routeTemplate: string.Concat("api/Home", "/{id}"),
defaults: new { id = RouteParameter.Optional, controllerName="Home" }
);
My controller class is called "HomeController". I'm trying to redirect all URLs to it.
Here is the code for HomeController. For now I have commented out the calls to external logic (remote controller). It should just be returning a string on the Get action.
public class HomeController : ApiController
{
private IExternalController<string> remoteController;
public HomeController()
{
remoteController = GlobalKernelConfiguration.GetStandardKernel().Get<IExternalController<string>>();
}
public string Get()
{
return "HELLO FROM INTERNAL"; //remoteController.Get();
}
public string Get(int id)
{
return remoteController.Get(id);
}
public void Delete(int id)
{
remoteController.Delete(id);
}
public void Post(string value)
{
remoteController.Post(value);
}
public void Put(int id, string value)
{
remoteController.Put(id, value);
}
}
I would expect http://localhost:9000/api/[AnythingHere] to route to the home controller but I get the following error when trying the following url: http://localhost:9000/api/Home
{"Message":"No HTTP resource was found that matches the request URI 'http://loca
lhost:9000/api/Home'.","MessageDetail":"No route providing a controller name was
found to match request URI 'http://localhost:9000/api/Home'"}
As #CodeCaster suggested in the comments the problem was caused by not using the correct parameter in the routing options.
This is what I had before
_configuration.Routes.MapHttpRoute
(
name: "DefaultApi",
routeTemplate: string.Concat("api/Home", "/{id}"),
defaults: new { id = RouteParameter.Optional, controllerName="Home" }
);
this is what I have now:
public static void AddControllerRoute(string controllerName)
{
_configuration.Routes.MapHttpRoute
(
name: "DefaultApi",
routeTemplate: string.Concat("api/Home", "/{id}"),
defaults: new { id = RouteParameter.Optional, controller ="Home" }
);
}
notice that the defaults parameter was changed and now uses "controller" instead of "controllerName" this solved the problem and it's now working.

web api not finding the Get method - 500 error

When i try to make a call to the web api to get a single vendor. It is return a 500 error.
I try to break inside the Get(Guid id) but it never gets inside of it.
I know it is getting to the controller but looks like it can't find the Get(Guid id) function.
Any ideas why? Am I missing something?
web api route
config.Routes.MapHttpRoute(
name: "Module",
routeTemplate: "api/module/{controller}/{id}/{action}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
);
api controller
namespace App.WebUI.Controllers.api.Module
{
public class VendorsController : ApiController
{
private readonly UnitOfWork _repository = new UnitOfWork();
// GET api/Module/vendors
[HttpGet]
public HttpResponseMessage Get()
{
return all;
}
// GET api/Module/vendors/5
[HttpGet]
public HttpResponseMessage Get(Guid id)
{
return single
}
// POST api/Module/vendors
[HttpPost]
public HttpResponseMessage Post(Vendor vendor)
{
insert single
}
[HttpPut]
//PUT api/Module/Vendors
public HttpResponseMessage Put(Vendor vendor)
{
update single
}
}
}
This is what i had to do to make it work.
config.Routes.MapHttpRoute(
name: "Module",
routeTemplate: "api/module/{controller}/{id}/{action}",
defaults: new { id = RouteParameter.Optional, action = "DefaultAction" }
);
Then at the default Action(GET(), GET(id), POST(), PUT(), DELETE()) I added the data annotation: [ActionName("DefaultAction")]

Overlapping routes with WebApi

I'm trying to add a special route to the default WebApi sample:
The regular ones are
/api/values (retrieves all values)
/api/values/{id} (retrieves a specific value (by numeric id))
Now I want to add this api:
/api/values/special
The previous api (/api/values/{id}) should serve all requests with a numeric id, but /api/values/special should serve requests that call that specific url.
So far I got this for routing:
config.Routes.MapHttpRoute("SpecialValues", "api/values/special", new { controller = "values", action = "SpecialValues" });
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And this for implementation:
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
// GET api/values/special
public IEnumerable<string> SpecialValues()
{
return new string[] { "special1", "special2" };
}
}
But it will render: The requested resource does not support http method 'GET'.
If I call /api/values/special and I add [HttpGet] to the SpecialValues method it will work
but /api/values will stop working saying that there are multiple matching actions.
The changes to WebApiConfig is not needed. Attach a Route attribute and a HttpGet attribute. You can read more about it here.
[HttpGet]
[Route("api/{controller}/special")]
public IEnumerable<string> SpecialValues()
{
return new string[] { "special1", "special2" };
}
WebApiConfig -
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
yes, as mentioned above, attribute based routing is the only way to go here...
this post may also interests you
Overload web api action method based on parameter type

Categories

Resources