Web api 2 attribute routing not working even with it configured in WebApiConfig - asp.net-web-api

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);
}

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; }
}

Passing config properties to controllers

I'm self-hosting Owin in a Windows service that provides some functionality and access to other Windows for other functionalities. I have a provider that's starting the Owin app and is also the entry point to all of the system's functionalities.
How can I pass a reference to my provider to each of the controllers? The ApiController class provides Configuration.Properies, but I can't find a way to add the provider reference to the configuration properties. The StartOptions class has Settings, but it only takes string values. I also don't see a way to pass an extra parameter to the Startup class' Configuration method.
public class Provider : BaseProvider
{
public override void StartUp()
{
var options = new StartOptions();
//StartOptions.Settings only allows string values
//I need to pass this provider to the controllers
WebApp.Start<StartUp>("http://SVS:1234");
}
}
internal class StartUp
{
private Provider _provider = null;
internal void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "POSEngineAPI",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//??? How can I set _provider when calling Startup.Config?
config.Properties.AddOrUpdate("Provider", _provider, (key, value) => _provider);
app.UseWebApi(config);
}
}
internal class SessionController : ApiController
{
[HttpPost]
public IHttpActionResult PostSession(Session session)
{
var provider = GetProvider();
//TODO
return Ok(session);
}
[HttpPut]
public IHttpActionResult PutSession(Session session)
{
var provider = GetProvider();
//TODO
return Ok(session);
}
private WebApiSignalRProvider GetProvider()
{
return Configuration.Properties["Provider"] as Provider;
}
}
I'm new to Web Api and Owin so I'm probably missing something obvious.

Post data through Url to webapi

Hej,
I am unable to post data to the action method through querystring to the action method which is located in the controller class below is my code.
I type a url "http://localhost:53459/api/esb/post/test" to post value and nothing happens
Any help would be appreciated.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{value}",
defaults: new { value = RouteParameter.Optional }
);
}
}
[RoutePrefix("api/esb")]
public class EsbController : ApiController
{
[Route("get")]
[HttpGet]
public string Get()
{
return "Hello there!";
}
[Route("post")]
[HttpPost]
[AcceptVerbs("POST")]
public string Post([FromUri]string value)
{
return string.Format("{0} is posted successfully ", value);
}
[Route("put")]
[HttpPut]
public string Put([FromUri] string value)
{
return string.Format("{0} is updated successfully ", value);
}
[Route("delete")]
[HttpDelete]
public string Delete(string value)
{
return string.Format("{0} is deleted successfully ", value);
}
}
If you are typing the url into a browser you are constructing a GET request so it will never reach your Post action. You can confirm this by adding "GET" to the allowed verbs on the action (note: remove the [HttpPost] attribute).
[Route("post")]
[AcceptVerbs("POST", "GET")]
public string Post([FromUri]string value)
{
return string.Format("{0} is posted successfully ", value);
}
remove parameter binding [FromUri] and update the Route like
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{value}",
defaults: new { value = RouteParameter.Optional }
);
}
}
Controller
[RoutePrefix("api/esb")]
public class EsbController : ApiController
{
[Route("get")]
[HttpGet]
public string Get()
{
return "Hello there!";
}
[Route("post/{value}")]
[HttpPost]
[AcceptVerbs("POST")]
public string Post(string value)
{
return string.Format("{0} is posted successfully ", value);
}
}
This is working for me, try to use Postman in chrome or fiddler.
POST http://localhost:XXXXX/api/esb/post/test HTTP/1.1
For dot.Net Core (I'm using v2.0), use FromRoute
[AllowAnonymous]
[HttpPost]
[Route("validateEmail/{validationCode}")]
public async Task<IActionResult> ValidateEmail(
[FromRoute] Guid validationCode)
{
await authService.ValidateEmailAsync(validationCode);
return Ok();
}
Then post like this:

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);
}

Resources