I'm trying to create a REST api based on the Asp.Net MVC4 web api framework. The GET requests are working just fine. All other verbs are getting ignored by the server.
It just says the following:
"No action was found on the controller 'Track' that matches the request."
Though the example error is from the Track controller all other controllers have the same problem.
This is the method on the controller Track I'm trying to call:
[HttpPost]
public Object Index(string token, string juke, string track)
{
}
I'v tried it using a JSON object like so:
{ "track": "0000", "juke": "0000" }
And I tried to use the "normal" way:
track=0000&juke=0000
The '0000' in the examples above are stand-ins for the real id's.
To be sure I'm also posting the Register() from WebApiConfig.cs:
public static void Register(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
config.Routes.MapHttpRoute(
name: "DefaultIndexBackEnd",
routeTemplate: "back-end/{controller}/{token}",
defaults: new { action = "Index", token = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultBackEnd",
routeTemplate: "back-end/{controller}/{action}/{token}",
defaults: new { token = RouteParameter.Optional }
);
}
Try this in your TrackController. Because you are using multiple parameters, they must be declared as optional.
[HttpPost]
public Object Index(string token="", string juke="", string track="")
{
}
You can make parameter token mandatory because token is declared as optional in routing configuration DefaultIndexBackEnd. I think using [FromBody] attribute is a good idea when there is more than one parameter for POST actions.
First, it's important to understand how a typical REST Web API is supposed to work. Typically, it uses different HTTP verbs (GET, POST, PUT, DELETE) for specific actions.
GET: get an entity (or a collection) from the server
POST: create a new entity
PUT: update an existing entity
DELETE: delete an existing entity
So when I see [HttpPost] on your Index action, it seems to me that the REST API pattern is broken.
Instead, in your controller you should have a Entity Get(int id) action (to get data) and a void Post(Entity entity) action to create new records.
No need to decorate your actions with HttpGet or HttpPost, the MVC Api framework will route the request to your action based on their names.
You can take a look at an example here.
After several hours of trying and researching articles I finally found an article that precisely described my problem! If you're having the same problems, check out the article.
The problem was that I had to use the [FromBody] attribute on one parameter of the of the action. After moving juke and track into a model it finally worked as I had hoped.
Thanks everyone for the help, you all set me on the right track!
Related
I want to create a method for the update in a particular field of my record.
I'm new with Web API 2 in ASP.Net. I created a method but always get's an error
{"Message":"The requested resource does not support http method 'GET'."}
I want to create a custom method like this:
[HttpPut]
public string AddDeviceID(string deviceID)
{
User user = db.Users.Single(x => x.MobileUserName == "Dev" && x.MobilePassword == "123");
user.MobileDeviceId = deviceID;
db.SaveChanges();
return "success";
}
Here is my route config:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{deviceId}",
defaults: new { deviceId = RouteParameter.Optional }
);
I don't know what am i missing on this code I'm just following the concept of ASP.Net MVC of creating a method for update.
Anybody could provide some ideas? Thanks in advance.
You need to call your api with a HTTP PUT Request. You get this error, because you do a HTTP GET Request.
Even if you accepted the other answer, you realy should use the HttpPut Attribute, because it is more correct in the meaning.
From wikipedia:
GET
The GET method requests a representation of the specified resource. Requests using GET should only retrieve data and should have
no other effect. (This is also true of some other HTTP methods.)[1]
The W3C has published guidance principles on this distinction, saying,
"Web application design should be informed by the above principles,
but also by the relevant limitations."
PUT
The PUT method requests that the enclosed entity be stored under the supplied URI. If the URI refers to an already existing resource,
it is modified; if the URI does not point to an existing resource,
then the server can create the resource with that URI.
Just google about how to send a HTTP PUT Request with the library/software you are testing.
By the way: Postman is a good tool for testing.
This error happened because you executed HTTP GET request but your action is decorated with [HttpPut] attribute and can accept only HTTP PUT. To fix this you can implement functionality to execute HTTP PUT request or you can decorate your action with [HttpGet] attribute like in the following example:
[HttpGet]
public string AddDeviceID(string deviceID)
{
User user = db.Users.Single(x => x.MobileUserName == "Dev" && x.MobilePassword == "123");
user.MobileDeviceId = deviceID;
db.SaveChanges();
return "success";
}
How you made the request? Please note that browser will makes HTTP GET request.
I have a case where I can't get a default route to work via Config.Routes.MapHttpRoute(), but if I put the route as a route attribute in the controller it works fine.
Global.asax
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//RouteConfig.RegisterRoutes(RouteTable.Routes);
//BundleConfig.RegisterBundles(BundleTable.Bundles);
}
(removed last two as they're non-WebAPI requirements (right?) same result even if I leave them in)
WebApiConfig.cs
public static void Register(HttpConfiguration config) {
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "v1/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Controller
public class ErrorsController : ApiController
{
[HttpGet]
[Route("v1/Errors/Get")]
public IHttpActionResult Get(int id) {
return Ok();
}
[HttpPost]
[Route("v1/Errors/Submit")]
public IHttpActionResult Submit()
{
// do stuff
return Ok();
}
}
If I have the attribute routes in there, everything works fine. If I don't, I get 404s. For example, the following two report a 404 error:
localhost:myport/v1/Errors/Get?id=5
localhost:myport/v1/Errors/Submit
Yet, if I submit this:
localhost:myport/v1/Errors
I get the following response:
No HTTP resource was found that matches the request URI
'http://localhost:59498/v1/Errors/'.
No type was found that matches the controller named 'v1'.
Obviously my route configuration isn't kicking in, but for the life of my I can't tell why. I even tried changing the route name to Default instead of DefaultApi, thinking that perhaps Default had some internal significance.
"Normal" routing doesn't support this kind of routes with prefix "v1" in it. In a way or another this kind of rounting follows the old MVC routing, where the first part of the address must be the controller; so when you set the address localhost:myport/v1/Errors the system is currently looking for a controller named "v1".
If you want to use a route with a prefix before you have to stick with the attribute routing; that's why it works perfectly with attributes and it doesn't without.
May I suggest you to use a "global" prefix for the v1 thing? That would allow you to avoid repeating the same part of the URL over and over again on different resources. I suggest you to check this article for a couple of implementation details.
I am coding a web api in c# and I have a question in regards to the correct route to access a function called test.
Here is the class definition:
[RoutePrefix("api")]
public class ItemsWebApiController : ApiController
I have a RoutePrefix as follows:
[Route("test")]
Here is the function called test:
[Route("test")]
[System.Web.Http.AcceptVerbs("GET")]
[System.Web.Http.HttpGet]
public String test()
{
return "test";
}
I am trying to access the following url:
http://localhost/api/test
The above url is displaying the following exception:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its
dependencies) could have been removed, had its name changed, or is
temporarily unavailable. Please review the following URL and make
sure that it is spelled correctly.
Requested URL: /api/test
How can I access the test function, such that the string "test" is displayed in my browser?
EDIT
I have deployed to my local IIS, and the database connection strings are working correctly.
The address for the local IIS is http://localhost/
These are the urls that I have tried:
http://localhost/test
http://localhost/api/test
http://localhost/api/test/test
http://localhost/ItemsWebApiController/test
http://localhost/ItemsWebApi/test
All of the above return the error page.
Thanks
If you a putting [Route("test")] your url will be http://address/test
if you need url like http://address/api/test, change your route like [Route("api/test")]
Note: you need to add [HttpGet] as well
You have to activate Attribute Routing in WebAPI Controllers
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
and in your application start
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
And then your URL is http://localhost/test, because the default route would not match here.
The route based on your configuration will be: http://address/api/test/test
the first test is the route prefix from the attribute [Route("test")];
the scond test is the action in the controller from the attribute defined on the method
[Route("test")]
public String test()
{
Web api infers the http method based on your action name. It won't know from "Test" what to use.
See the docs
HTTP Methods
Web API also selects actions based on the HTTP method of the request (GET, POST, etc). By default, Web API looks for a case-insensitive match with the start of the controller method name. For example, a controller method named PutCustomers matches an HTTP PUT request.
You can override this convention by decorating the mathod with any the following attributes:
[HttpDelete]
[HttpGet]
[HttpHead]
[HttpOptions]
[HttpPatch]
[HttpPost]
[HttpPut]
The following example maps the CreateBook method to HTTP POST requests.
[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }
I have built a service in .NET 4.5 and Entity Framework 6, using the ASP.NET Web API template. When I make a GET request to the service and omit a required parameter, it is returning a 404 - Not Found, instead of a 400 - Bad Request. I tried checking to see if the required parameters are null, but it's not even reaching the code inside the method (see code below). My question is how do I change the default response or make it return 400 for missing required parameters.
[Route("item")]
[HttpGet]
public IHttpActionResult GetItem(string reqParam1, string reqParam2)
{
if (reqParam1 == null || reqParam2 == null)
throw new HttpResponseException(HttpStatusCode.BadRequest);
//remainder of code here
}
Here is the webAPI.config file. I don't think that I have modified it from the default.
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 }
);
}
}
Just a note: the API isn't accessing information such as item/electronics/tvs but more like items where item is green and is square, where those descriptions are mandatory to pass in - so the query is like /item?color=green&shape=square.
The server returns a 404 because it cannot find the route you are trying to access. Actually the arguments should be a part of your route, as optionals or not: Route("items/{reqParam1}/{reqParam2}")
If you defined them as optionals the route should still be called if you use just items and you can throw the BadRequest. If not optionals then probably the NotFound will still be thrown.
You should also post the webApi config, so that we can see the base route mapping.
EDIT
take the following route example:
config.Routes.MapHttpRoute(
name: "RunsWithParams",
routeTemplate: "api/{controller}/{project}/{skip}/{take}",
defaults: new { take = RouteParameter.Optional, skip = RouteParameter.Optional }
);
the project param is mandatory, the remaining are optional. you can call with just the project, or the remaining, and if you use just one of the Optionals then 404 is thrown, because the route is not defined.
This is the same as using the decorators, so it does not exactly solve your issue, but explains a bit better the routing. Still, I don't see why you need a BadRequest in particular.
Yet another edit:
To use the request url parameters as you mention at the end of the post, you just need to use the [FromUri] decorator, see this page for more details, and a breef example follows:
// POST api/files/{project}?target={target}
public HttpResponseMessage Post(string project, string target, [FromUri]string fname){...}
I have a controller List
[Route("api/cache/list")]
[HttpGet]
public IEnumerable<string> List()
{
...
}
But i get a 404 if i try to go to localhost:12121/api/cache/list. My webapi config looks like this:
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.Formatters.Remove(config.Formatters.XmlFormatter);
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
}
}
On the other hand if i change that controller to
[Route("api/cache/list/{id}")]
[HttpGet]
public IEnumerable<string> List(int id =0)
{
...
}
It will work. Ive tried doing it without the id as a parameter, still doenst work. What am I doing wrong here?
Extra Info:
I do have a strange setup. My WebApiConfig.cs and global.cs are in a different project. The project which has my controller will then reference the project with the WebApiConfig.cs
Rather than giving the param a default value, try making your id param nullable:
public IEnumerable<string> List(int? id)
Then check for null in code. This also helps differentiate a passed zero from an omitted param value.
I wasnt able to figure out how to fix the problem exactly but I did find a way around my problem.
First I correctly identified that when my routing was in a single assembly the exact same code worked fine. When the routing for my project in a different assembly makes it difficult for config.MapHttpAttributeRoutes(); to correctly identify attribute routes.
On the other hand using a RoutePrefix seemed to fix my issue.
Im not going to set this as my answer because I feel it doesn't truly address the problem, but I felt it would be informational to anyone who comes along this question.