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" }
);
Related
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'
I know that when migrating from asp.net 2.2 to 3 app.UseMvc() line in startup.cs file's configure method is replaced by app.UseRouting(); app.UseAuthorization(); app.UseEndpoints();.
Can anyone explain how app.UseEndpoints() and app.UseMvc() works internally ?
Under the asp.net core mvc 2.2 framework, to use traditional routing, you must configure the IRouteBuilder interface in the UseMVC middleware.
In the Configure method of the application Startup, the default routing settings are as follows:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
{
In the call to UseMvc, MapRoute is used to create a single route, also known as the default route. Most MVC applications use routing with templates. A convenient method for the default route can be used:
app.UseMvcWithDefaultRoute();
UseMvc and UseMvcWithDefaultRoute can add an instance of RouterMiddleware to the middleware pipeline. MVC does not directly interact with middleware, but uses routing to process requests. MVC connects to the route through an instance of MvcRouteHandler.
UseMvc does not directly define any routes. It adds placeholders {controller=Home}/{action=Index}/{id?} to the route collection of attribute routing. By overloading UseMvc(Action), users are allowed to add their own routes, and attribute routing is also supported.
UseEndpoints: Perform matching endpoints.
It separates routing matching and resolution functions from endpoint execution functions, and until now, these functions are bundled with MVC middleware.
First of all,you could have a look at their source code:
public static IApplicationBuilder UseEndpoints(this IApplicationBuilder builder, Action<IEndpointRouteBuilder> configure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
VerifyRoutingServicesAreRegistered(builder);
VerifyEndpointRoutingMiddlewareIsRegistered(builder, out var endpointRouteBuilder);
configure(endpointRouteBuilder);
// Yes, this mutates an IOptions. We're registering data sources in a global collection which
// can be used for discovery of endpoints or URL generation.
//
// Each middleware gets its own collection of data sources, and all of those data sources also
// get added to a global collection.
var routeOptions = builder.ApplicationServices.GetRequiredService<IOptions<RouteOptions>>();
foreach (var dataSource in endpointRouteBuilder.DataSources)
{
routeOptions.Value.EndpointDataSources.Add(dataSource);
}
return builder.UseMiddleware<EndpointMiddleware>();
}
ASP.NET Core 3 uses a refined endpoint routing which will generally give more control about routing within the application. Endpoint routing works in two separate steps:
In a first step, the requested route is matched agains the configured routes to figure out what route is being accessed.
In a final step, the determined route is being evaluated and the respective middleware, e.g. MVC, is called.
The two steps are set up by app.UseRouting() and app.UseEndpoints(). The former will register the middleware that runs the logic to determine the route. The latter will then execute that route.
Also, refer to:
https://asp.net-hacker.rocks/2019/08/12/aspnetcore30-look-into-startup.html https://aregcode.com/blog/2019/dotnetcore-understanding-aspnet-endpoint-routing/
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" }
);
});
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
My site is largely a suite of web services exposed via the Asp.Net Web API. There are also pages, designed to support the webservices (testing etc), written in Razor (and implicitly Asp.Net MVC 4).
For the XML versions of the webservices I have a schema-export action (uses the XsdDataContractExporter) which is picked up by my standard API route (although note - I've flipped the precedence of the Web API and Pages):
//page routes
routes.MapRoute(
"Default", // Route name
"pages/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional
} // Parameter defaults
);
//an additional route for my Schema controller action
routes.MapHttpRoute("XSD", "schema.xsd",
new { controller = "schema" });
//API Catch-all Route
routes.MapHttpRoute("APIMain", "{controller}/{id}",
new { id = RouteParameter.Optional });
Now on a razor page I want to emit a link to the 'friendly' schema URL ~/Schema.xsd. Anticipating issues with route discovery I immediately went for hitting the route directly by name:
#Html.RouteLink("Schema", "XSD");
However this just emits a link equivalent to ~/.
I've tried some other combinations of route values - but it appears MVC's HtmlHelper and UrlHelper simply don't want to pick up Web API routes.
I'm sure if I cracked open the source of Asp.Net MVC 4 I'd find the reason - but I'm hoping somebody already knows, and since I can't find another SO about such cross-linking I figured it'd be a good addition to the SO library.
I should add that browsing to ~/Schema and ~/Schema.xsd do correctly display the XML schema produced by the API action.
Update
Post-RC a method was added to MVC's UrlHelper, HttpRouteUrl, which does exactly the same thing I suggest here in this answer. This is my discussion thread over on CodePlex where I was told this. So there is no need for you to use the magic string mention here in generating links to Web API routes.
Original answer
I've managed to get it to work - although it might not by the time MVC 4 is RTMd (disclaimer disclaimer!)
I changed my Html.RouteLink call as follows:
#Html.RouteLink("XML request schema", "XSD", new { httproute = true })
I didn't originally intend to answer my own question straight away - but having done some research I found an answer.
First I verified that the HtmlHelper's route collection is the same as the RouteTable.Routes collection (i.e. contained all routes).
Following the call-chain through, I remembered having trawled through the current Web API and page MVC 4 source code from CodePlex, that HttpRoutes (in System.Web.Http.Routing) need a 'hidden' route value to be added otherwise they will never match. Here's the source code from lines 21-25 of HttpRoute class (correct as of 8th June 2012 source):
/// <summary>
/// Key used to signify that a route URL generation request should include HTTP routes (e.g. Web API).
/// If this key is not specified then no HTTP routes will match.
/// </summary>
internal const string HttpRouteKey = "httproute";
A bit of further analysis of the code showed that it expects this route value to be a boolean.
Clearly, this is something that can be turned into extension methods - perhaps Html.HttpRouteLink (and Html.HttpActionLink) - with extra extensions on UrlHelper for hiding the magic string for the route data value.