allow multiple get methods in same controller web api asp.net - asp.net-web-api

public class UserController : BaseApiControllerAuth
{
public ArrayList GetPermissions()
{
enter code here
}
public UserModelCollection Get()
{
}
public UserModelCollection Get(fromuri id)
{
}
}
my webapiconfig file Route is
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "Defalutapi2",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "All", id = RouteParameter.Optional }
);
1.api/user/ is working
2.api/user/123 is working
3.api/usr/Permissions gives error Multiple actions were found that match the request
how i can solve this ???

Why you do not use web api 2 attributes routing instead of the traditional one?
You can do the following using attribute routing:
[HttpGet]
[Route("ObtainData")]
public async Task<IHttpActionResult> ObtainData(string input){}
[HttpGet]
[Route("ObtainData2")]
public async Task<IHttpActionResult> ObtainData2(string input){}

Related

Web API multiple get

I have couple of get methods in my web API class. The route config is as below;
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/test/{action}/{id}",
defaults: new { controller = "item", action="testBring", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
The controller class is as below;
public class ItemController : ApiController
{
// GET: api/Item
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[ActionName("testBring")]
[HttpGet]
public string testBring()
{
return "value";
}
// GET: api/Item/5
public string Get(int id)
{
return "value";
}
// POST: api/Item
[HttpPost]
public void CreateItem([FromBody]string value)
{
}
// PUT: api/Item/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Item/5
public void Delete(int id)
{
}
}
When I try to call /api/test, testBring method gets called. But when I call /api/Item I get an error that multiple actions were found that match the request ... Why is it so?
/api/test maps fine to the first route template api/test/{action}/{id} because of the default parameters included, which allow it to call a specific action.
However, /api/Item maps to the second route template api/{controller}/{id} and the route table is unable to determine which action you wanted since there are multiple HttpGet actions (ie Get() and testBring() ) that match the requested path.

How do I route to a method in ASP.NET Web API?

According to the documentation, if I have this in my WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I should be able to route to a method in my API controller using a URL like this:
http://localhost:55601/api/Customers/Search
Here is my method:
[ResponseType(typeof(int))]
[HttpPost]
public IHttpActionResult Search([FromBody]CustomerDTO SearchTerm)
{
string Name = SearchTerm.Name;
string Email = SearchTerm.Email;
string PhoneNumber = SearchTerm.Phone;
var customer = db.Customers.Single(c => c.Name == Name && c.EmailAddress == Email && c.PhoneNumber == PhoneNumber);
return Ok(customer.id);
}
I'm sending the search data as a JSON object (using HTTP POST method) in the request body.
However, I get an error saying:
Multiple actions were found that match the request
I only have one method in this controller called Search.
I would have thought this should be pretty straightforward, and work the same way it does with MVC controllers. But I think I'm missing something obvious. Can anyone tell me what it is?
EDIT: As per #KevinLaw's request, adding code for controller showing upblic methods. Also, for further information the following request (HTTP GET) works as expected:
http://localhost:55601/api/Customers?email=[recipient#domain]
public class CustomersController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: api/Customers
public IQueryable<Customer> GetCustomers()
{
//...
}
// GET: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomer(int id)
{
//...
}
// GET: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomerByEmail(string email)
{
//...
}
// PUT: api/Customers/5
[ResponseType(typeof(void))]
public IHttpActionResult PutCustomer(int id, Customer customer)
{
//...
}
// POST: api/Customers
[ResponseType(typeof(Customer))]
public IHttpActionResult PostCustomer(Customer customer)
{
//...
}
[ResponseType(typeof(int))]
[HttpPost]
public IHttpActionResult SearchCustomer([FromBody]CustomerDTO SearchTerm)
{
//...
}
// DELETE: api/Customers/5
[ResponseType(typeof(Customer))]
public IHttpActionResult DeleteCustomer(int id)
{
//...
}
}
The problem here is that the WebApiController uses the REST API specs.
Which state that in a Web Api Controller there can be Zero - One Http Verb.
What i mean by that is that you can have one GET,PUT,POST,DELETE,PATCH
The reason you don't have any problem with the GET is because you have them correctly overloaded. () (int) (string).
But your Posts is (Customer) (CustomerDTO) They are both complex objects and the Binder cannot identify which is which when binding to the complex object.
For this to work you need to use Route Attributes or explicit route.
Attribute Routing
Explicit Routing pt1
Explicit Routing pt2
I think the links are enough to get you started.
If you still want to see some code on your specific case leave a comment below and i will give you some examples.
Thanks
EDIT: Added Examples
Attribute Routing
On WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
}
On Controller
[RoutePrefix("api/test")]
public class TestingController : ApiController
{
[HttpGet]
[Route("")]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
[HttpPost]
[Route("search")]
public IHttpActionResult Post([FromBody]SearchCriteria criteria)
{
return Ok(criteria);
}
}
public class SearchCriteria
{
public string Name { get; set; }
}
Explicit Routing
On WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes config.Routes.MapHttpRoute(
name: "SearchTest",
routeTemplate: "api/test/search",
defaults: new { controller = "Testing", action = "Search" }
);
config.Routes.MapHttpRoute(
name: "TestingController",
routeTemplate: "api/test/{id}",
defaults: new { controller = "Testing", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
On Controller
public class TestingController : ApiController
{
[HttpGet]
public IEnumerable<string> Get()
{
return new[] { "value1", "value2" };
}
[HttpPost]
public IHttpActionResult Search([FromBody]SearchCriteria criteria)
{
return Ok(criteria);
}
}
public class SearchCriteria
{
public string Name { get; set; }
}

Web api 2 attribute routing not working even with it configured in WebApiConfig

I don't understand why my attribute routing isn't working. If I go to 'http://localhost:56125/api/Contact/1682' then I get the test controller with "value1" and "value2" shown, but if I go to http://localhost:56125/api/contacts/1682 then I get No HTTP resource was found that matches the request URI 'http://localhost:56125/api/contacts/1682' and I don't understand why?
In ContactController.cs:
public class ContactController : ApiController
{
private readonly NGSystemRepository _repo = new NGSystemRepository();
// GET: api/Contact
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[Route("api/contacts/{contactId}")]
[HttpGet]
public ContactInformation GetContactInformation(int contactNumber)
{
return _repo.GetContact(contactNumber);
}
}
In WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Argh! Stupid mistake. I didn't match the attribute property name (contactId) with the method property name (contactNumber).
My controller action should simply be:
[Route("api/contacts/{contactId}")]
[HttpGet]
public ContactInformation GetContactInformation(int contactId)
{
return _repo.GetContact(contactNumber);
}

how to do a post to url of pattern /api/{controller}/{action}/{id}?

I have my controller method written as follows:
public class WorkerController : ApiController
{
//POST
//api/worker/unregister/<id>
//http body:
//status=<status>
[HttpPost]
public void Unregister(int status)
{
//stuff
}
}
Whenever I make a request, it says resource not found (404).
What is the correct way of wiring this?
Edit: wiring the call using PostMan chrome extension - rest client:
http://localhost:xxxx/api/worker/unregister/3
...and then I pass post parameters as usual. Its x-www-form-urlencoded
Update:
Tried the following:
public class WorkerController : ApiController
{
[HttpGet]
public void Unregister(int id, int status)
{
//stuff
}
}
...and passed the url as below:
http://localhost:xxxx/api/worker/unregister/3?status=-1
This worked!
But why didn't the other work. Here is code in the WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ServiceApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
In your controller you have used status as parameter name whereas in WebApiConfig.cs you have used id as parameter name
change your WebApiConfig.cs like this
config.Routes.MapHttpRoute(
name: "ServiceApi",
routeTemplate: "api/{controller}/{action}/{status}",
defaults: new { status = RouteParameter.Optional }
);
Ah, I figured it out!
You have to pass a model.
public class fooModel
{
public int status {get; set;}
}
The action should be re-written as follows:
public void unregister(int id, fooModel m)
{
Debug.Print(m.status);
}

Overload web api action method based on parameter type

Is there a way to perform parameter type based overloading of an Action method ?
i.e. Is it possible to do the following in a Controller
public class MyController : ApiController
{
public Foo Get(int id) { //whatever }
public Foo Get(string id) { //whatever }
public Foo Get(Guid id) { //whatever }
}
If so, what changes need to be made to the Route table.
This kind of scenario is not well supported by the standard routing methods.
You may want to use attribute based routing instead as this gives you a lot more flexibility.
Specifically look at the route constraints where you can route by the type:
// Type constraints
[GET("Int/{x:int}")]
[GET("Guid/{x:guid}")]
Anything else will turn into a bit of a hack... e.g.
If you did attempt it using standard routing you would probably need to route to the correct action via it's name, then use reg ex's constraints (e.g. guid) to route to the required default action.
Controllers:
public class MyController : ApiController
{
[ActionName("GetById")]
public Foo Get(int id) { //whatever }
[ActionName("GetByString")]
public Foo Get(string id) { //whatever }
[ActionName("GetByGUID")]
public Foo Get(Guid id) { //whatever }
}
Routes:
//Should match /api/My/1
config.Routes.MapHttpRoute(
name: "DefaultDigitApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetById" },
constraints: new { id = #"^\d+$" } // id must be digits
);
//Should match /api/My/3ead6bea-4a0a-42ae-a009-853e2243cfa3
config.Routes.MapHttpRoute(
name: "DefaultGuidApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetByGUID" },
constraints: new { id = #"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$" } // id must be guid
);
//Should match /api/My/everything else
config.Routes.MapHttpRoute(
name: "DefaultStringApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetByString" }
);
Updated
I would normally use a POST if doing a FromBody (perhaps use the FromUri with the model instead) but your requirements could be met by adding the following.
For the controller
[ActionName("GetAll")]
public string Get([FromBody]MyFooSearch model)
{
if (model != null)
{
//search criteria at api/my
}
//default for api/my
}
//should match /api/my
config.Routes.MapHttpRoute(
name: "DefaultCollection",
routeTemplate: "api/{controller}",
defaults: new { action = "GetAll" }
);
You can code your methods as below
[Route("api/My/{id:int}")]
public string Get(int id)
{
return "value";
}
[Route("api/My/{name:alpha}")]
public string Get(string name)
{
return name;
}
[Route("api/My/{id:Guid}")]
public string Get(Guid id)
{
return "value";
}

Resources