Web Api Route Config ASP-NET MVC - c#

We are developing an ASP.NET MVC application, and we decide separate our API Services/Controllers inside a folder named for example API_Services instead put them directly in the controllers.
The problem is: how we set/define the route for that? Tipically is like the following code (at App_Start folder and WebApiConfig.cs file) :
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
We try set the routeTemplate like:
routeTemplate: "API_Services/api/{controller}/{id}",
Or this:
routeTemplate: "api/API_Services/{controller}/{id}",
Doesn't work... someone can help us? Thank you!

If I understand the question right:
if you use some web api controllers I would recommend to read the book "ASP.NET Web API 2: Building a REST Service from Start to Finish 2nd Edition By Jamie Kurtz , Brian Wortman". There is the chapter about api versions and the author describes routing through folders (V1 folder, V2 etc).
Also You can use just "inline-attribute" routing.
And You can read about Areas (or just try to put some "namespaces" into the routing settings or play with it ). I hope it helps, sorry for my English.

We are strucure like this:
Controllers (Folder)
A_Controller
B_Controller
API (Folder)
A_Controller
B_Controller
After a lot of google search, I found this:
Controllers (Folder)
A_Controller
B_Controller
API (Folder)
A_Controller
B_Controller
(which apparently works, but just tomorrow I go can test and then I give feedback. Anyway thanks for other solutions. Thank you!)

The changes you are talking about are to your folders, not the routes, which the conventions will ignore.
The way it works means that whatever project folder you move it into, if it is named XXXController it will be found without route changes. So with the following this:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
http://yoursite.com/api/XXXController will still find your controller even if it is in another folder:
You can play around with defaults if you want more control but I don't think this is what you are asking for. For example:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/API_services/{id}",
defaults: new { controller="mycontroller" id = RouteParameter.Optional }
So whenever people visit http://yoursite.com/api/API_services, the controller used would be "mycontroller".

Related

Restrict API route to controller namespace with ASP.NET

I have found a similar question that relates to standard asp.net controllers, but i have tried it and it doesn't seem to be working with the apicontroller.
Restrict route to controller namespace in ASP.NET Core
I want to allow for an API to be deprecated in the future without needing to change the url. To solve this i will put a version prefix at the start of the route. This way i can add some modifications to an endpoint without breaking any integrations with this API.
config.Routes.MapHttpRoute(
name: "V1 API",
routeTemplate: "v1/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "V2 API",
routeTemplate: "v2/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I want to keep the same controller names and in most cases everything will be the exact same, the response and request body will be the only things that change. I have tried to add a subdirectory in the controllers folder in order to add v1 and v2 to the namespace of the controller, but i get an error about there being 2 controllers having the same name.
How can i modify the routes so that it will point to the v1 namespace for the controller instead of just searching for any class within the controller. I dont want to have to add the version to the controller name as i want to make the revision updates as seamless as possible.
I think you could solve this issue by adding the route prefix to each controller.
Can you use a URL query parameter?
https://localhost:8000/{controller}/{action}/{id}?version=?
You could then use control flow within the controller to determine what logic to use, based on the version.

ASP.Net MVC IdentityServer3 broke my webapi routing

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.

ASP.NET MVC WebAPI 404 error

I have an asp.net web forms application running under v4.0 integrated mode.
I tried to add an apicontroller in the App_Code folder.
In the Global.asax, I added the following code
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
When I tried to navigate to the controller at http://localhost/api/Value, I get the 404 error.
The extensionless url is configured in the handler section. I have forms and anonymous authentication enabled for the website.
ExtensionLess url is configured for '*.'
When I hit the url for controller, the request is handled by StaticHandler instead of ExtensionlessUrlHandler-Integrated-4.0.
I have no clue now why the system will throw the error as shown in the image below.
I was experiencing this problem.
I tried editing my WebApiConfig.cs to meet a number of recommendations here and code samples elsewhere. Some worked, but it didn't explain to why the route was not working when WebApiConfig.cs was coded exactly as per the MS template WebApi project.
My actual problem was that in manually adding WebApi to my project, I had not followed the stock order of configuration calls from Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// This is where it "should" be
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// The WebApi routes cannot be initialized here.
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I could make guesses about why this is, but I didn't investigate further. It wasn't intuitive to say the least.
The problem is in your routing configuration. Mvc routing is different from WebApi routing.
Add reference to System.Web.Http.dll, System.Web.Http.Webhost.dll and System.Net.Http.dll and then configure your API routing as follows:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
Ensure the following things
1.) Ensure that your IIS is configured with .NET 4.5 or 4.0 if your web api is 4.5 install 4.5 in IIS
run this command in command prompt with administrator privilege
C:\Windows\Microsoft.NET\Framework\v4.0.30319>aspnet_regiis.exe -i
2.) Change your routing to
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
and make request with Demo/Get (where demo is your controller name)
if the 1,2 are not working try 3
3.) Add following configuration in web.config file
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
Also, make sure your controller ends in the name "Controller" as in "PizzaPieController".
I tried all of the above and had the same problem. It turned out that the App pool created in IIS defaulted to .net 2.0. When I changed it to 4.0 then it worked again
Thanks Shannon, works great =>
My order in my Global.asax was :
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
instead of the good one :
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
Also try to delete your entire api bin folder's content. Mine was containing old dlls (due to a big namespace renaming) exposing conflicting controllers. Those dll weren't deleted by Visual Studio's Clean functionality.
(However, I find asp.net web api seriously lacks routing and debugging information at the debugging level).
If you create the controller in App_Code how does the routing table know where it is? You have specified the route as "api/{controller/..." but that's not where the controller is located. Try moving it into the correct folder.
After hours of spending time on this , i found the solution to this in my case.
It was the order of registering the Routes in RouteConfig .
We should be registering the HttpRoute in the Route table before the Default controller route . It should be as follows. Route Config Route table configuration
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Thanks to Shannon, ceinpap, and Shri Guru, the following modification works for me:
WebApiConfig.cs:
public static void Register(HttpConfiguration config)
{
...
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Global.asax.cs:
protected void Application_Start()
{
...
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
...
}
For the URL you've trying (http://localhost/api/Value) make sure there's a public type named ValueController which derives from ApiController and has a public method with some of these characteristics:
Method name starts with Get (e.g. GetValues or simply Get).
There's an HttpGet attribute applied to the method.
In case you're trying the code from the default Web API project template, the name of the controller is ValuesController, not ValueController so the URL will be http://localhost/api/values.
If non of the above helps, you may want to enable tracing which can give you a useful insight on where in the pipeline the error occurs (as well as why).
Hope this helps.
None of solutions above solved my problem... My error was that I copied the bin files directly to production server, and then, I don't work. The 404 was gone when I publish the project to disk and copied the "published" folder to server. It's a little obvious, but, can help some one.
I copied a RouteAttribute based controller dll into the bin folder, but it wasn't getting recognized as a valid controller and I was getting the 404 error on the client.
After much debugging, I found my problem. It was because the version of System.Web.Http.dll that the controller was referencing was different from the version of System.Web.Http.dll that the main project (the one containing global.asax.cs) was referencing.
Asp.Net finds the controller by reflection using code like this
internal static bool IsControllerType(Type t)
{
return
t != null &&
t.IsClass &&
t.IsVisible &&
!t.IsAbstract &&
typeof(IHttpController).IsAssignableFrom(t) &&
HasValidControllerName(t);
}
Since IHttpController is different for each version of System.Web.Http.dll, the controller and the main project have to have the same reference.
We had this as well, changing .NET version from 4.5 to 4.5.1 or newer solved the issue
The sequence of registering the route, was the issue in my Application_Start().
the sequence which worked for me was
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
earlier it was
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
Try just using the Value part of the controller name, like this:
http://localhost/api/Value
Note: By convention, the routing engine will take a value passed as a controller name and append the word Controller to it. By putting ValueController in the URI, you were having the routing engine look for a class named ValueControllerController, which it did not find.
Your route configuration looks good. Double check the handlers section in web.config, for integrated mode this is the proper way to use ExtensionLessUrlHandler:
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
More on this topic:
http://blogs.msdn.com/b/tmarq/archive/2010/05/26/how-extensionless-urls-are-handled-by-asp-net-v4.aspx
Time for me to add my silly oversight to the list here: I mistyped my webapi default route path.
Original:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/id",
defaults: new { id = RouteParameter.Optional}
);
Fixed: (observe the curly braces around "id")
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional}
);
I appreciate this is a very old question but I thought I would add another answer for future users.
I found this to happen just now in a project I was working on only after it was deployed to CI/Staging. The solution was to toggle the compilation debug="true" value back and forth while deploying each version to each environment once, and it would fix itself for me.
In my case I forgot to make it derive from ApiController.
So it would look like
public class ValuesController : ApiController

Routing with action after id parameter in Web API

In web api the default route is:
/api/locations/123?days=5
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But what if I wanted the route to look like this /api/locations/123/events?days=5 while still being able to hit the LocationsController with a route like this /api/locations/123?state=md
Controller:
public class LocationsController : ApiController {
// GET api/locations/123/events?days=5
public IEnumerable<Event> GetEventsByDays(int idLocation, int days) {
// do stuff
}
// GET api/locations/123?state=md
public IEnumerable<Location> GetLocationsByState(string state) {
// do stuff
}
}
There is really two questions here:
Does it make sense to have a LocationsController that returns events or should there be a completely separate controller for that?
How would the route in the WebApiConfig be setup to allow for routes like this?
You're talking about two differents things :
Routing
Routing is used in ASP.NET MVC & Web Api to map URLs directly to a controller and/or an action. This is especially useful for readability, as the developer can focus on designing URLs that are human readable (for example, product support and search engine indexing). What is important here, is that there is no unique relation between a route and a controller. You can create 10 routes if you want that will map the same controller/action.
For example, your two routes can be like this
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{idLocation}/events/{days}",
defaults: new { id = RouteParameter.Optional }
);
Also note that /api/locations/123?state=md is not correct for the default route template. It's /api/locations/123. Because you have an extra parameter in the url, you will execute GetLocationsByState.
Controller
It's recommanded to have a single responsiblity per controller and to keep it as small as possible. Your business logic should be somewhere else.
Jeffrey Palermo (creator of Onion Architecture) say
if you can’t see a ASP.NET MVC action method on a screen without having to scroll, you have a problem
At the end, as you everything, you can do what you want without paying attention if it's good or bad. The difficulty is not always to setup an architecture but to maintain and to follow your own rules.
I hope this will help you.
I suppose you are not so familliar with routing, so do not hesitate to read intro and action selection.
You can have a separate controller for events, if you would want to manipulate events independent of location. Please see if this is of help.

How to design for non-CRUD operations in MVC4's Web API?

I am starting a new project using ASP.NET MVC4 and Visual Studio 2012. In terms of API design, most examples focus on basic CRUD operations via PUT, GET, POST and DELETE verbs on entities (as you would expect). I was reading the following:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
The article suggests that if I choose to map a route as
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{action}",
defaults: new { action = RouteParameter.Optional }
);
This is more of an RPC style approach; from which I inferred they are recommending two routes with two controllers to split up each operation:
Perhaps something like for parent entity CRUD:
routes.MapHttpRoute(
name: "Parent",
routeTemplate: "api/{controller}/{id}/{action}",
defaults: new { id = RouteParameter.Optional }
);
and for child entity CRUD:
routes.MapHttpRoute(
name: "Child",
routeTemplate: "api/user/{id}/{controller}",
defaults: new { id = RouteParameter.Optional }
);
From a data/crud perspective this makes total sense. However, what about when you want to perform a non-crud operation on an entity (i.e., /User/NoahBawdy/SignIn or /User/NoahBawdy/ChangePassword)? I could see these being a PUT or POST action, but does it really require it's own controller? Is this the wrong way to approach the API design for these types of operations?
Any insight greatly appreciated as always.
You raised some interesting points in your post. I was faced with a similar challenge on a project I am working on and my approach is to add an action parameter on the routing configuration.
With that change i could add any method to the controller and call it from the client. This avoids the need to specify multiple controllers for methods that conceptually belongs in the same controller.
Omar

Categories

Resources