So this has really been causing me a headache.
Route:
config.routes.MapHttpRoute(
name: 'ControllerAction' ,
routeTemplate: "api/{controller}/{action}"
);
Controller:
public class LookupsController : ApiControllerBase
{
[ActionName("all")]
public Lookups GetLookups()
{
var lookups = new Lookups
{
Customers = GetCustomers().ToList(),
//add more
};
return lookups;
}
}
but whenver I try to hit the uri: /api/lookups/all
I get a 404 error saying:
"No action was found on the controller 'Lookups' that matches the name 'all'."
Any help would be appreciated
EDIT:
So I figured it out finally.
it was because of the wrong depency.
VS2012 autoresolved the action to system.web.mvc.actionnameattribute while what I needed was system.web.http.actionnameattribute.
weird problem, anyways, I hope this helps someone else.
Jakob Li,
EDIT:
You are renaming the controller but not specifying the method. WebApi resolves the method from the prefix GET, PUT, DELETE, POST. So the method attribute must be placed at the signature of the action:
Try this, works well for me:
public class LookupsController : ApiControllerBase
{
[HttpGet()]
[ActionName("All")]
public Lookups GetLookups()
{
var lookups = new Lookups
{
Customers = GetCustomers().ToList(),
//add more
};
return lookups;
}
}
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Take a look here for more information about routing:
Routing in ASP.NET Web API
Hopes its help you!
Related
I cannot figure out why I am getting a 404 for this request. I am pretty new to Web Api routing but I thought this was correct. Anyone have an idea of what the issue is. I have been on this for about an hour and a half. Thank you!
Routing Config:
public override void RegisterArea(AreaRegistrationContext context)
{
RouteTable.Routes.MapHttpRoute(
"japiClientOnboarding",
"japi/clientOnboarding/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
Controller Action:
[System.Web.Http.HttpPost]
public HttpResponseMessage createClientFromProspect(long prospectId)
{
........
}
My call via Postman:
Figured it out. Had to do with my parameter name not being "whitelisted" in our system. Very frustrating!
I am trying to achieve api versioning using a CustomHttpControlSelector and AttributeRouting on asp.net webapi.
What i am trying to do is distinguish controller's versions by it's namespaces.
if a request is made to /api/v2/foo/bar
i want it to match
namespace Web.Controllers.Api.v2
{
[RoutePrefix("foo")]
public class LongerThanFooController : ApiController
{
[HttpGet]
[Route("bar")]
public string BarFunction()
{
return "foobar";
}
}
}
but as i see when i don't use full url on RoutePrefix (/api/v2/foo) attribute routing doesn't kick in and i get null when i call
request.GetRouteData().GetSubRoutes();
on my CustomHttpControlSelector. i don't want to Repeat /api/v2 on every controller.
if i decide to remove attributeRouting and use manual routes like
config.Routes.MapHttpRoute(
name: "DefaultVersionedApi",
routeTemplate: "api/v{version}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, version = Config.LatestVersion }
);
i lose all flexibility of naming my controllers and functions.
is there a way to get out of this limbo?
note: for the CustomHttpControlSelector i modified code on http://aspnet.codeplex.com/SourceControl/changeset/view/dd207952fa86#Samples/WebApi/NamespaceControllerSelector/NamespaceHttpControllerSelector.cs
I realize this is bit of an old question now, but it can answered using the ASP.NET API Versioning package for ASP.NET Web API. In the latest 3.0 version, you can achieve your scenario by updating your configuration with:
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
configuration.AddApiVersioning(
options =>
{
options.Conventions.Add( new VersionByNamespaceConvention() );
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionSelector = new CurrentImplementationApiVersionSelector( options );
} );
configuration.MapHttpAttributeRoutes( constraintResolver );
You should also remove your convention-based routes. Those are unnecessary if you are using attribute routing.
The setup for your controller simply changes to:
namespace Web.Controllers.Api.v2
{
[RoutePrefix("api")]
public class LongerThanFooController : ApiController
{
[HttpGet]
[Route("foo/bar")]
[Route("v{version:apiVersion}/foo/bar")]
public string BarFunction()
{
return "foobar";
}
}
}
The reason you need two route definitions is that you cannot have default values in the middle of the route template. Default values can only be used at the end. This also means that you need to allow no API version to be specified and indicate the way to determine which API version should be selected is to use the current implementation (e.g. latest). I'm personally not a fan of this approach because I think things should be predictable to clients, but this will achieve your desired result.
I have made a project using ng.Net template from visual studio, i got it up and running, i added a new ProgramController.cs, Program.cshtml template, programCtrl.js angular controller, and a program angular module.
And it just will not work.
I have a ASP.NET web api and angularjs on clientside.
Here are 2 example routes:
$routeProvider.when('/todomanager', {
templateUrl: 'App/TodoManager',
controller: 'todoManagerCtrl'
});
$routeProvider.when('/program', {
templateUrl: 'App/Program',
controller: 'programCtrl'
});
And what they do backend:
[HttpGet]
[Authorize]
public List<Program> GetPrograms()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.Programs;
//return db.Programs;
}
[HttpGet]
[Authorize]
public List<todoItem> GetUserTodoItems()
{
string userId = Request.GetOwinContext().Authentication.User.Identity.GetUserId();
var currentUser = UserManager.FindById(userId);
return currentUser.todoItems;
}
The first one works, the second one gives tpload compile error (could not find template view)
I can get the TodoManager view if I call it in /program
But I cant reach my program.cshtml. It's in the same folder as TodoManager.cshtml
I could provide more code, But I dont know which, Because it all works.
My closest guess so far, is that I dont have access to that view, I'm being blocked by a blockviewhandler or the view doesnt exist.
I have the access.
If it was the viewblockhandler it would also block TodoManager.cshtml
And it exists xD I'm looking at it..
It's like i'm spamming 4 + 4 on a calculator and it keeps returning 5..
Please, anything, been stuck for 2 workdays.
EDIT additional code
//RouteConfig RegisterRoutes
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "App",
url: "{url}",
defaults: new { controller = "Main", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Main", action = "Index", id = UrlParameter.Optional }
);
}
//WebApiConfig Register
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();
//I chaned the routeTemplate so that methods/services would be identified by their action, and not by their parameters.
//I was getting conflicts if I had more than one GET services, that had identical parameter options, but totally different return data.
//Adding the action to the routeTemplte correct this issue.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}", //routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
//blockviewhandler in Web.config
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*." verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
Edit: Project structure, i have a HomeController that returns my Index.cshtml in there, i have my ng-view which is where my views should be rendered.
Many seem to misunderstand my real question, i want to know:
How is it, that the todoManager works, and the program does not.
When using ASP.NET MVC, Web API etc. you should add "every" view to the mvc controller, you probably have something like this in one of your controllers:
public ActionResult TodoManager()
{
return PartialView();
}
assuming you are following a tutorial or modifying a sample it is probably in Controllers/AppController. If true you must add your another view as:
public ActionResult Program()
{
return PartialView();
}
I am trying to reach a specific webapi action using: api/error/LogJsError with some formdatacollection. I have an ErrorController like this:
public class ErrorController : ApiController
{
[System.Web.Http.HttpPost]
public void LogJsError(FormDataCollection form)
{
var s = form.Aggregate("Javascript error: message", (current, i) => current + (": " + i.Value));
new Logger(HttpContext.Current).LogException(new Exception(s));
}
}
and the routes are configured like this:
config.Routes.MapHttpRoute(
name: "ApiWithAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In WebApiConfig.Register and
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
inside the RouteConfig.RegisterRoutes
But whatever i do the routecollection returns controller = "Api" which thus results in a 404. What am i doing wrong, why is the api route not uses?
Duuuh...i found the answer right after posting it on: https://msdn.microsoft.com/en-us/library/cc668201%28v=vs.140%29.aspx
Note this: "To avoid having the wrong handler handle a request, you must consider all these conditions when you define routes. The order in which Route objects appear in the Routes collection is significant. Route matching is tried from the first route to the last route in the collection. When a match occurs, no more routes are evaluated. In general, add routes to the Routes property in order from the most specific route definitions to least specific ones."
My MVC route (eg: {controller}/{action}/{id}) was added to the routecollection before the (more specific) WebApi route (eg: api/{controller}/{action}/{id}). So instead of using:
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
In Application_Start(), use:
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
I am developping a WEB API using asp.net and my goal is to be able to call this type of url's:
/html/countries/...
/json/countries/...
Countries is a controller, and depending on the parameter before it returns different result.
What i did and it doesn't seems to work:
routes.MapRoute(
name: "Default",
url: "api/{action}/{controller}",
defaults: new
{
}
);
CountriesController:
[ActionName("html")]
public string get()
{
//...
}
[ActionName("json")]
public void getType()
{
//...
}
Any sugestions?
EDIT:
I have like 7 controllers.
And there are some possible urls:
/html/{controller}/x/y
/json/{controller}/x/y/order/h
/html/{controller}/x/z/order/y/j
/json/{controller}/x/z/order/y/j
Allow me by start saying that if "html" or json action means "format" those should not be part of your controller they are media types and needs to be configured differently
Web Api v1 defines resources globally in the global.asax on application_start event. Assuming you are using Visual Studio 2013 and based Microsoft 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: "htmltApi",
routeTemplate: "html/{action}/{controller}",
);
config.Routes.MapHttpRoute(
name: "jsonApi",
routeTemplate: "json/{action}/{controller}",
);
...
Another important detail is that WebApi v2 introduced something called Route Attributes 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 };
}
...
Thanks to the Dalorzo answer i did find the problem:
The problem occurred because my application was created the following way:
which resulted in creation of both files, RouteConfig.cs (MVC) and WebApiConfig.cs (WEB API):
What is WRONG is that the code in the question is from RouteConfig.cs
After putting the code
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "api/{action}/{controller}",
defaults: new
{
action = "html"
}
);
in WebApiConfig.cs, it worked proper way.