Web API Route with optional prefix-- how to handle? - asp.net

I'm implementing a REST API using ASP.NET's Web API framework. My API routes have the typical format,
$BASE_URI/api/resource1...
$BASE_URI/api/resource2...
$BASE_URI/api/resource3...
etc. Most (if not all) routes also have an optional prefix with an ID:
$BASE_URI/api/prefix/{id}/resource1...
$BASE_URI/api/prefix/{id}/resource2...
$BASE_URI/api/prefix/{id}/resource3...
When the prefix is not supplied, the API is supposed to use a default value (like 0) and the response does not require authorization. When the prefix is supplied, the request must be authorized.
The prefix gets handled in the same way every time (but obviously the different resources get handled differently). Is there a way for me to parse the prefix component of the route separately, and then handle the route suffix normally? I am trying to avoid the situation where I have duplicate route entries for every resource, where the only difference is the prefix handling.
If you think this is bad design, I'd like to hear about that too.
Thanks in advance.

You need to define two separate routes, there's no other way around.
config.Routes.MapHttpRoute(
name: "DefaultApiWithPrefix",
routeTemplate: "api/prefix/{prefixId}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

If you're using Web API 2, below is a guide to Attribute Routing, otherwise there is no simple work around.
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Related

Is it possible to inject / use services as routing paramters?

I am working with Symfony 6. The configuration of a route allows to add extra parameters which are passed to the controller:
index:
path: /somepage
controller: App\Controller\PageController::index
defaults:
title: 'Hello world!'
Is it possible to use a service as parameter? Something like:
defaults:
foo: '%some.container.parameter%' << Works, use container parameter?
logger: '#logger' << FAILS, use services as parameter?
This example does not work. While container parameters are correctly resolved, the services parameter is interpreted as string #logger instead of being resolved to the logger service.
Of course one could inject the complete service container into the PageController and use $this->container->get($logger); to get the services by its id. However, I found several sources, that injecting the services container is a bad idea which should be avoided.
So, is it possible to use a services as routing parameter?
Background
I would like to use a service parameter to make the controller more flexible when being used at different places.
For example the RedirectController can be used to create simple redirects without having to create a different controller for each redirected route.
Assume that the redirection of some routes is critical for some reason and needs more verbose logging and for example an alert notification via email.
Instead of creating different controllers and injecting different loggers, one could solve this by using different loggers as parameter:
legacy_one:
path: /somepage
controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController
defaults:
logger: '#defaultLogger'
legacy_two:
path: /otherpage
controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController
defaults:
logger: '#alertLogger'

Disable all server routing except for api calls in asp.net

I have created a angular2 project using ASP.net with webapi. I have everything setup correctly and working but when I am routing in angular2 such as: "localhost:1234/login" the source of the page is displaying the error:
HTTP Error 404.0 - Not Found
This is obviously because of server routing that cant find any path for this. My question is: Is there any way to disable all routing by the server except for if I'm doing a call like: "localhost:1234/api/login" because that would entail making a server request to a webapi controller which will return JSON. I want angular2 to handle all forms of routing. And the server just to handle data being pushed out.
Thank you in advance
If Angular is generating views by consuming APIs this should not be an issue. If the server is expected to serve up HTML for the routes Angular is calling then it needs equivalent routing configured.
Yes you can do this in your startup.cs class.
You need to configure the way your API calls are made in a way, and redirect to your index.html otherwise like so :
app.UseMvc(r =>
{
r.MapRoute(
name: "default",
template: "{controller}/{path?}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" }
);
r.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" }
);
});

ASP.NET Web API 2 re-routing

I am building a restful services for my data using ASP.NET web api 2.
In the data model, I have an entity called "ServiceProvider" which provide "Service"(s).
so, I am building restful for those two entities.
the requirements is to build two restful services, one as follow
api/serviceProvider/{id}/services
which gives list of services provided by that provider.
as well, the requirements is to give the same data (services by provider) through this rest service
api/services?serviceProviderId={providerId}
so, I created two controllers, for "serviceProvider", and for "Service"
I already implemented the second call, by creating an action method in the "Service" controller with this signature
public IEnumerable<Service> GetServiceByProvider(int providerId)
because both are going to run the same code, can I just re-route the first call to the second one?
At the same time, not effecting the other actions in both controllers??
I tried to do this is the webApiConfig with no success
config.Routes.MapHttpRoute(name: "serviceByProvider",
routeTemplate: "api/serviceProvider/{id}/services",
defaults: new { controller = "Service", action = "GetServicesByProvider" });
where the "GetServiceByProvider" is the method name in the "Service" controller that will serve the second call
but without success
As I said there are other actions in both controllers I don't want to affect them by this routing.
Like I have the following
api/serviceProvider
api/serviceProvider/{id}
api/services/
api/services/{id}
.....
I decided to try again and duplicate your environment and found a problem in your route template. The parameter your route is expecting is called id but your GetServicesByProvider method accepts a parameter called providerId. The framework is looking for parameters with the same name. Changing this should get your routing working the way you want it to AND maintain all of your previous routes.
config.Routes.MapHttpRoute(
name: "serviceByProvider",
routeTemplate: "api/serviceProvider/{providerId}/services",
defaults: new { controller = "Service", action = "GetServicesByProvider" }
);

404 after setting non-default ASP.net MVC routes

I'm using ASP.net MVC4 with VWD Express 2010. I've set up an extra route to remove the extraneous /Home/ from URLs, as suggested for example here and here. I made sure to put them in the right order:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Home",
url: "{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
This works as far as removing /Home/ is concerned, but now I get 404 when trying to access non-default controllers:
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: /LogItem/
I've tracked it down to the route, since if I remove the non-default one, it works fine again. I'm not sure what I'm doing wrong however, and by my understanding of other answers and the docs, this should work.
Edit: Since it's apparently not clear from my description, I added the "Home" route to be able to link to /About/ instead of /Home/About, etc. It was the suggested solution I found after googling. I basically want it to match actions implemented by HomeController with shorthand URLs (/{action}), as well as any other controller I also add in full (/{controller}/{action}/{optional id})
Routes are evaluated on a first-come, first-served basis. Your "Home" route basically says "If you see a URL with just one segment - grab it and handle it, then assume that segment is the action. When that happens, the second route you've registered doesn't get a chance to handle the request.
With the specific error as an example, you are requesting "/LogItem". The Home route kicks in, parses "LogItem" as the name of the action, and passes it to the default controller. Since that controller maybe doesn't have a LogIn action, a 404 is thrown.
One solution is to change the order of the routes. But even then, those two are overlapping, since the 2nd will also handle URLs with 1 segment, but interpret that segment as the controller. It will execute the default action (Index) rather than passing the handling to the next route.
Why do you need the first route? What are you trying to achieve? If you provide more context we can figure out a way to implement those routes.

Calling MVC4 web api controller in MVC3

I am using MVC 3 application and used web api controller.
I want to call a method with parameters .
But when I try to call the method I get Internal Server error(500)
My global.asax is:
routes.MapHttpRoute(
name: "PostMethod2",
routeTemplate: "api/mycontroller/{a}/{b}",
);
Should I add any code to application_start of Global.asax to route to the parameterized method?
Your route configuration doesn't seem legit, a proper one would look like below
routes.MapHttpRoute(
name: "PostMethod2",
routeTemplate: "api/{controller}/{action}/{a}",
);
Note, "{controller}" and "{action}" can't be random. MVC uses it to map to your controller and action(method) respectively. {a} will be mapped to your parameter "int a". MVC uses mapping by convention.
RouteTable.Routes.MapPageRoute("Profile", "{Link}.{Id}.aspx", "~/Profile.aspx");
Example be helpful!
Link and Id is argument

Resources