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.
Related
I am having the issue of getting a 404 on my web api routing. Although there are many posts about this issue, they mainly seem to be about changing the order of the routes so that the MVC routes don't override the api routes.
I have tried all of the solution I have come across yet nothing seems to fix my issue.
Here is my controller:
[RoutePrefix("api/paving-designer")]
public class PavingDesignerController : ApiController
{
[HttpGet]
[Route("get-forms/{userId}")]
public IHttpActionResult GetForms(Guid userId)
{
ICollection<PavingDesignerFlatForm> forms = _helper.GetForms(userId);
if (forms != null)
{
return Ok(forms);
}
else
{
return NotFound();
}
}
}
And this is my Web Api Config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new
{
id = RouteParameter.Optional
});
}
}
and this is my global asax
private void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
// RouteConfig.RegisterRoutes(RouteTable.Routes);
// Initialize Castle & install application components
_bootstrapper = CastleInitialiser.Initialise();
}
As you can see I have even tried to comment out the mvc routes to see if that made a difference
If I browse to http://localhost/api/paving-designer/get-forms/c6c489a7-46c6-420e-9e39-56797c8094cf
I get the following error:
No type was found that matches the controller named 'paving-designer'.
I have tried changing the route prefix to the following but to no avail
/api/paving-designer
/paving-designer
paving-designer
And if I browse to http://localhost/api/pavingdesigner/get-forms/c6c489a7-46c6-420e-9e39-56797c8094cf
I get the following error:
Multiple types were found that match the controller named 'pavingdesigner'. This can happen if the route that services this request ('api/{controller}/{action}/{id}') found multiple controllers defined with the same name but differing namespaces
I don't see how I can have multiple controllers as this is the only one I have.
Can anyone see where I am going wrong?
You are using both routing types.
Using attribute routing defined next route:
/api/paving-designer/get-forms/{userId}
Using default routing there is other route:
/api/{controller}/{action}/{id}
These routes are have the same template.
But using the first of them - ControllerSelector can not find paving-designerController.
Using the second - there is no action named get-forms. There are GetForms
If you remove one of them - it should work.
Ok in my particular case, the error was being caused as my IoC was registering the controller twice.
This was causing the duplicate entries, which in turn made the attribute routing fail.
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.
So I am currently implementing security on a project I am working on and I followed the guide for identityServer3 to add it to my mvc5 application. I got through the complete setup and thought everything was good, until I realized that routes in my api, unless they were the very basic ones, /api/.../ no longer work.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I am using the default routing, and on the various pieces of my api controllers I have put route attributes to guide them in the event they fall outside of this format. for example:
[Route("api/Location/GetByAreaIncludeFileStore/{id}")]
public IEnumerable<Location> GetLocationsByAreaIdIncludeFileStore(int id)
{
if (id <= 0)
{
return null;
}
IEnumerable<Location> locations = _lookupService.GetLocationsByAreaIdIncludesFileStore(id);
return locations;
}
and as i said earlier, prior to adding identity server theses worked beautifully. During the addition of IdentityServer I had to add a few nuget packages to my webapi:
install-package Microsoft.Owin.Host.SystemWeb
install-package Microsoft.Aspnet.WebApi.Owin
install-package Thinktecture.IdentityServer3.AccessTokenValidation
So basically my question after all is said and done is, How can I fix my routes so I can get all of the information I need?
Currently I have routes that are
api/controller
api/controller/id
api/controller/action
api/controller/action/id
Any Help would be amazing, Thanks!
Also, I looked through many of the other posts and tried a lot of variations of routing and attributes before asking this question.
Add this line in your WebApiConfig config.MapHttpAttributeRoutes(); before your config.Routes.MapHttpRoute().
I was actually able to get it working using the method described in the solution in this post: MVC 4.5 Web API Routing not working?
I tried doing this yesterday, and it didn't seem correct, but with a little more spit and polish I ended up achieving proper routes. They recommended having the route config as follows:
config.Routes.MapHttpRoute(
name : "DefaultAPi",
routeTemplate : "api/{controller}/{id}/{action}",
defaults: new {id= RouteParameter.Optional,
action = "DefaultAction"
);
and then following that pattern change all the basic routes like:
[ActionName("DefaultAction")
public string Get()
{
}
[ActionName("SpaceTypes")]
public string GetSpaceTypes(int id)
{
}
It worked, I had to refactor all of my api calls and my restful services to match, but nonetheless, I am back to functioning.
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 solution with two projects. One Web Api bootstap project and the other is a class library.
The class library contains a ApiController with attribute routing.
I add a reference from web api project to the class library and expect this to just work.
The routing in the web api is configured:
config.MapHttpAttributeRoutes();
The controller is simple and looks like:
public class AlertApiController:ApiController
{
[Route("alert")]
[HttpGet]
public HttpResponseMessage GetAlert()
{
return Request.CreateResponse<string>(HttpStatusCode.OK, "alert");
}
}
But I get a 404 when going to the url "/alert".
What am I missing here? Why can't I use this controller? The assembly is definitely loaded so I don't think http://www.strathweb.com/2012/06/using-controllers-from-an-external-assembly-in-asp-net-web-api/ is the answer here.
Any ideas?
Try this. Create a class in your class library project that looks like this,
public static class MyApiConfig {
public static void Register(HttpConfiguration config) {
config.MapHttpAttributeRoutes();
}
}
And wherever you are currently calling the config.MapHttpAttributeRoutes(), instead call MyApiConfig.Register(config).
One possibility is you have 2 routes on different controllers with the same name.
I had 2 controllers both named "UploadController", each in a different namespace and each decorated with a different [RoutePrefix()]. When I tried to access either route I got a 404.
It started working when I changed the name of one of the controllers. It seems the Route Attribute is only keyed on the class name and ignores the namespace.
We were trying to solve a similar problem.
The routes within the external assembly were not registering correctly.
We found one additional detail when trying the solution shown on this page.
The call to the external assembly "MyApiConfig.Register" needed to come before the call to MapHttpRoute
HttpConfiguration config = new HttpConfiguration();
MyExternalNamespace.MyApiConfig.Register(config); //This needs to be before the call to "config.Routes.MapHttpRoute(..."
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);