Question:
Why does my action Add gets hit instead of my delete action?
Error message:
"ExceptionMessage": "Multiple actions were found that match the
request: Add on type blog.Controllers.api.UsersController Delete on
type blog.Controllers.api.UsersController
My angular delete
$scope.delete = function (email) {
$http.post('/api/users/Delete', email).success(function() {
initData();
});
}
alternative tested
//$http({
// url: '/api/users/Delete',
// method: 'POST',
// data: { 'userEmail': email }
// })
// .success(function(data, status, headers, config) {
// initData();
// }).
// error(function(data, status, headers, config) {
// $scope.showError = true;
// });
My MVC 5 api controller
private readonly UserDataAccess _userDataAccess;
// GET: Users/Add
[HttpPost]
public HttpResponseMessage Add(User user)
{
_userDataAccess.AddUser(user);
return Request.CreateResponse(HttpStatusCode.OK);
}
// GET: Users/Delete - never gets hit
[HttpPost]
public HttpResponseMessage Delete([FromBody]string userEmail)
{
_userDataAccess.DelereUserByEmail(userEmail);
return Request.CreateResponse(HttpStatusCode.OK);
}
Please note I do not want to solve it using routing.
In WebApi you can only have one POST action per Routing. In order to have more than one for the controller class you will need to define new routings.
If you are using WebApi 2 you can benefit from the RoutingAttribute and define these new sub-routes
In your controller class use RoutePrefixAttributte:
[RoutePrefix('api/users')]
public class Users: ApiController{ ...
Followed by your 2 sub-routes:
[Route('add')]
[HttpPost]
public HttpResponseMessage Add(User user){..
[Route('delete')]
[HttpPost]
public HttpResponseMessage Delete([FromBody]string userEmail) {
About Routings
Web Api v1 defines resources globally in the global.asax on application_start event. Assuming you are using Visual Studio 2013 and based on Microsoft's default template your method may look like this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
WebApi routing configuration occurs here WebApiConfig.Register while your MVC configuration occurs here RouteConfig.RegisterRoutes
Your WebApi routing configuration should look like this
public static class WebApiConfig{
public static void Register(HttpConfiguration config){
config.Routes.MapHttpRoute(
name: "apiSample",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
...
As mentioned previously WebApi v2 introduced the Route Attributes and those can be used along with your Controller class and can facilitate the routing configuration.
For example:
public class BookController : ApiController{
//where author is a letter(a-Z) with a minimum of 5 character and 10 max.
[Route("html/{id}/{newAuthor:alpha:length(5,10)}")]
public Book Get(int id, string newAuthor){
return new Book() { Title = "SQL Server 2012 id= " + id, Author = "Adrian & " + newAuthor };
}
[Route("json/{id}/{newAuthor:alpha:length(5,10)}/{title}")]
public Book Get(int id, string newAuthor, string title){
return new Book() { Title = "SQL Server 2012 id= " + id, Author = "Adrian & " + newAuthor };
}
...
You may also refer this other POST with a similar question
How do I write REST service to support multiple Get Endpoints in .net MVC Web API
[HttpPost]
[Route('api/User/Delete/{userEmail}')]
public HttpResponseMessage Delete([FromBody]string userEmail)
{
}
You can manually configure the route as well
You can add the [HttpDelete] attribute on your delete method in your Api controller and then call it with $http.delete instead.
[HttpDelete]
public IHttpActionResult Delete(string email) {
return StatusCode(HttpStatusCode.NoContent);
}
To quote the docs of HttpDeleteAttribute Class.
Represents an attribute that is used to restrict an action method so that the method handles only HTTP DELETE requests.
Related
I am trying to just do a simple file upload API using Web API.
Here is the Controller:
[RoutePrefix("api/resize")]
public class ResizeController : ApiController
{
[HttpPost, Route("api/resize/preserveAspectRatio")]
public async Task<IHttpActionResult> resizePreserveAspectRatio()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
int maxWidth = 100;
int maxHeight = 100;
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
var buffer = await file.ReadAsByteArrayAsync();
//Do whatever you want with filename and its binaray data.
}
return Ok();
}
}
This is my WebApiConfig:
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}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
When I POST a file with PostMan, here is the error I get:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:26303/api/resize/preserveAspectRatio'.",
"MessageDetail": "No type was found that matches the controller named 'resize'."
}
This is not a dupe - was not able to find another article that addresses this specific combination.
This as you would expect is a routing issue. The comments have already identified that you have conflicts with your route and route prefix attributes resulting in the following route
api/resize/api/resize/preserveAspectRatio
being mapped to your action.
To get the desired route, you can either remove the prefix from the controller itself.
//removed prefix
public class ResizeController : ApiController {
//Matches POST api/resize/preserveAspectRatio
[HttpPost, Route("api/resize/preserveAspectRatio")]
public async Task<IHttpActionResult> resizePreserveAspectRatio() {
//...removed for brevity
}
}
Or Remove it from the route on the method
[RoutePrefix("api/resize")]
public class ResizeController : ApiController {
//Matches POST api/resize/preserveAspectRatio
[HttpPost, Route("preserveAspectRatio")]
public async Task<IHttpActionResult> resizePreserveAspectRatio() {
//...removed for brevity
}
}
Or override the route prefix by using tilde (~) on the method attribute
[RoutePrefix("api/resize")]
public class ResizeController : ApiController {
//Matches POST api/resize/preserveAspectRatio
[HttpPost, Route("~/api/resize/preserveAspectRatio")]
public async Task<IHttpActionResult> resizePreserveAspectRatio() {
//...removed for brevity
}
}
Reference Attribute Routing in ASP.NET Web API 2
I am using VS 2017 community. I have been building web api s for years. But something must have changed as I cannot get the simplest example to work.
I have a simple controller in the controller folder
public class TestApi : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
I have the necessary code in application start:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
But when I try and test the web api with a get like:
http://localhost:54014/api/testapi
I always get an xml message
Error
Message
No HTTP resource was found that matches the request URI
'http://localhost:54014/api/testapi'.
/Message
MessageDetail
No type was found that matches the controller named 'testapi'.
/MessageDetail
/Error
Here is the WebApiConfig.cs
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}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
I am a couple of hours into head scratching on this. As I say I have built many MS web api implementations and this one has me baffled.
You should add Controller suffix to your class name.
public class TestApiController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
When the app starts, the asp.net mvc frameworks looks for classes (which are inherited from ApiController or Controller) with this suffix and register them when building the route table. When a request comes to your app, the framework again look into the route table and direct the request to the corresponding controller and action method.
Make this change, rebuild your project and try again.
In addition to the already provided answer (which is correct by the way) you can also consider using attribute routing over convention-based routing, where in this case it would not matter what the name of the controller is.
You already have it enabled Based on the WebApiConfig.cs and
config.MapHttpAttributeRoutes();
So now it is just a matter of putting the attributes where needed
[RoutePrefix("api/testapi")]
public class TestApi : ApiController {
[HttpGet]
[Route("")] //Matches GET api/testapi
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
}
Reference: Attribute Routing in ASP.NET Web API 2
I am having a problem when trying to have two different GET methods, one for retrieving a number of resources while one is for retrieving a specific resource.
Startup.cs
config.Routes.MapHttpRoute("DefaultAPI",
"api/{controller}/{action}",
new { id = RouteParameter.Optional });
Controller.cs
[RoutePrefix("api/Files")]
public class FileController : ApiController
{
// /api/Files/
[Authorize]
[Route("")]
public IHttpActionResult GetAll()
{
}
// /api/Files/Id/
[Authorize]
[Route("Id")]
public async Task<HttpResponseMessage> Get([FromBody] string id)
{
}
// /api/Files/Upload
[Authorize]
[HttpPost]
[Route("Upload")]
public async Task<HttpResponseMessage> Post()
{
}
// /api/Files/Delete
[Authorize]
[Route("Delete")]
public IHttpActionResult Delete([FromBody] string id)
{
}
This is new to me and I know I am also making a mistake with using both IHttpActionResult as well as HttpResponseMessage but I figured I would change that later on after I figure out the routing.
Error:
When Startup.cs has
"api/{controller}/{action}"
, it returns a 404 Not found,
when it is "api/{controller}/{id}", the error is:
Multiple actions were found that match the request: \r\nGetAll
If you are using attribute-based routing, you need to add the following code before you declare any WebAPI routes in Startup.cs:
config.MapHttpAttributeRoutes();
I have two Wep APIs. I have done CRUD operation using one eg. Customer. But when I built another Similar Web API and called a method It shows:
{,…} Message: "No HTTP resource was found that matches the request URI
http://localhost:23995/Product/Insert'."
MessageDetail: "No route providing a controller name was found to
match request URI '[[same link as above here]]'"
Here is my JS Calling Method:
$scope.Insert = function () {
$http({
method: 'post',
url: 'http://localhost:23995/Product/Insert',
data: JSON.stringify($scope.Product)
}).then(function (response) {
alert("chec");
});
}
In Product Controller
// Insert
[HttpPost]
[Route("{controller}/Insert")]
public string Insert([FromBody] Product newProd) {
newProd.Insert();
return newProd.DbResponse;
}
In supplier Controller
// Insert
[HttpPost]
[Route("{controller}/Insert")]
public string Insert([FromBody] Product newProd) {
newProd.Insert();
return newProd.DbResponse;
}
Assuming you already have attribute routing enabled.
Attribute Routing in ASP.NET Web API 2
To enable attribute routing, call MapHttpAttributeRoutes during
configuration. This extension method is defined in the
System.Web.Http.HttpConfigurationExtensions class.
using System.Web.Http;
namespace WebApplication
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
}
and assuming that given the route you are getting the error on
http://localhost:23995/Product/Insert
Your product controller should look something like this.
[RoutePrefix("product")]
public class ProductController : ApiController {
// ... other code removed for brevity
// Insert
// eg: POST /product/insert
[HttpPost]
[Route("insert")]
public string Insert([FromBody] Product newProd) {...}
}
and your supplier controller would look very similar
[RoutePrefix("supplier")]
public class SupplierController : ApiController {
// ... other code removed for brevity
// Insert
// eg: POST /supplier/insert
[HttpPost]
[Route("insert")]
public string Insert([FromBody] Product newProd) {...}
}
you calling JavaScript should then be able to properly call the desired methods
Do you have a controller named "ProductController" with a method named Insert?
Looks like that's all that is missing for you.
I have a controller called News in my WebAPI project, I have two default actions called Get that handled the following URL's:
/api/News <- this returns a list of news
/api/News/123 <- this returns a specific news item by id.
All straightforward so far and obviously the default route handles these scenarios. I next want to have a URL that looks like this:
/api/News/123/Artists <- will return all artists related to the specified news item.
Now I am fairly news to ASP.Net MVC and WebAPI so this is the first time I have had to deal with routing. I have modified my default route to look like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{id}/{action}",
defaults: new { controller = "News", action = "Get", id = UrlParameter.Optional }
So here I have moved the {action} to the end of the URL and I have added a Artists method to my News controller. This still works with the first two scenarios but returns a 404 for the third scenario.
Obviously the routing isn't working for /api/News/123/Artists but I have no idea why.
I can't seem to find any examples of people using WebAPI like this which makes me think I am doing something fundamentally wrong.
Any help would be appreciated.
The issue is, that you are trying to acces Web API but mapping the ASP.NET MVC
this is a mapping you need:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{action}",
defaults: new {controller = "News", action = "Get", id = RouteParameter.Optional}
);
And it should be done in the App_Start \ WebApiConfig (if using the default template settings)
Example of the methods (in your news API controller):
// GET api/values/5
public string Get(int id)
{
return "value " + id;
}
// GET api/values/5
[HttpGet]
public string Artist(int id)
{
return "artist " + id;
}
The AttributeRouting should be a good solution. It can be installed by Nuget, and the document is here.
Some examples
public class SampleController : Controller
{
[GET("Sample")]
public ActionResult Index() { /* ... */ }
[POST("Sample")]
public ActionResult Create() { /* ... */ }
[PUT("Sample/{id}")]
public ActionResult Update(int id) { /* ... */ }
[DELETE("Sample/{id}")]
public string Destroy(int id) { /* ... */ }
[Route("Sample/Any-Method-Will-Do")]
public string Wildman() { /* ... */ }
[GET("", ActionPrecedence = 1)]
[GET("Posts")]
[GET("Posts/Index")]
public ActionResult Index() { /* ... */ }
[GET("Demographics/{state=MT}/{city=Missoula}")]
public ActionResult Index(string state, string city) { /* ... */ }
}
It works very well about custom routing.
Update
In asp.net WebApi 2, AttributeRouting is included inside by native. It has some history, the first version, asp.net WebApi 1, is weak about routing annotations.
And then, asp.net WebApi 2 is released, the AttributeRouting is included by native. So, that open project is not maintained anymore, said in GitHub page.
In microsoft blog, the section Independent Developer Profile – Tim McCall – Attribute Routing in MVC and Web API 2 said about the history too.
In routing Action is the action name on the method that you want to route to .That action name should be in the attribute used on the method.
[HttpGet]
[ActionName("CustomAction")]
public HttpResponseMessage MyNewsFeed(Some params)
{ return Request.CreateResponse(); }
Now your route should look like this
routes.MapRoute(
name: "Default",
url: "{controller}/{id}/{action}",
defaults: new { controller = "News", action = "CustomAction", id = UrlParameter.Optional
Let me know if this helps.