I want to be able to annotate some of my controllers with #SomethingProvider so that they automatically serve POST /{original-resource-url}/something URL and tie this URL to a generic handler.
Using a controller with wildcard mapping would not work because not all the resources will have that "something" collection available. Ex:
POST /user/something
POST /product/something
etc.
but not
POST /anything/something
My idea is to scan my controllers at runtime and look for that #SomethingProvider annotation so I can programmatically register the additional "something" endpoints.
I know it has to do with RequestMappingHandlerMapping but I can't figure out how to tap into it to add mappings.
Thanks geniuses
Related
I am working on SaaS application where I have implemented ASP.Net Web API as a service layer. In my Web API, when any request generated, it will have one header value "x-companyid" which is company specific and identify request comes from which company (Tenant).
I require that companyID in all my ApiControllers in Web API project. Of course I can get header value in every ApiController by using "Request.Headers.GetValues("x-companyid") but it will be repeated in all ApiControllers.
I have tried to create "BaseApiController" and inherit all my ApiControllers from "BaseApiController" but it's not allowing me to override ActionExecuting so that I can extract header at common place.
Can anyone suggest how can I extract "x-companyid" header commonly in my project so that I don't have to repeat code in all ApiController?
Your Best friend are action filters.
as they intercept each api call for specific controllers.
All u had to do is to decorate the controler name with something u made like
[Tenant]
public foo MyController()
{}
this approach gives you controll over what controllers you wish to add this extract to happen becouse maybe youll need some lookup apis and stuff that dont need to be for a specific Tenant
here is a very Helpful link :
https://www.c-sharpcorner.com/article/filters-in-Asp-Net-mvc-5-0-part-twelve/
Update
since there is a only one company for each instance
i would recommend adding a singleton for the current tenant u can access that anyway
https://en.wikipedia.org/wiki/Singleton_pattern
I'm using the Symfony CMF Routing Bundle to create dynamic routes (I'm using one example here):
$route = new Route('/dynamic-url');
$route->setMethods("GET");
$route->setDefault('_controller', 'AppBundle:MyRoute:getResponse');
$routeCollection->add('my-dynamic-route', $route);
The response is loaded from the getResponseAction() function inside the MyRouteController:
/**
* No annotations here, because I want the url to be dynamic from the database
*/
public function getResponseAction(Request $request) {
return $this->render('dynamic-page-template.html.twig');
}
When I go to '/dynamic-url', it works.
When in another controller, I want to redirect to this dynamic route, like this:
return $this->redirectToRoute('my-dynamic-route');
But I get this error: "None of the chained routers were able to generate route: Route 'my-dynamic-route' not found"
Also interesting: when I go to '/dynamic-url', the dev bar actually says that the Route name is 'my-dynamic-route'.
Edit
When I load all the routes, I don't see my dynamic route names:
$this->get('router')->getRouteCollection();
I think they should be in this list.
Since it's a dynamic route, which wasn't saved anywhere (like routing.yml ) it will be only availabe for Request where it has been defined. So at the end of Request your app will immediately "forget" about new Route generated at runtime.
When I load all the routes, I don't see my dynamic route names:
$this->get('router')->getRouteCollection();
I think they should be in this list.
Actualy No. It depends on where you call $this->get('router')->getRouteCollection();
Just try to call
dump($this->get('router')->getRouteCollection();)
right before the return statement in your Action where you're adding the my-dynamic-route route. I'm sure you'll see your my-dynamic-route in the list of routes, but if you call it anywhere else - you won't see it.
It's less about symfony rather about stateless nature of web (see Why say that HTTP is a stateless protocol?)
I started to think about this and pointed your question to an routing issue on symfony-cmf. You tagged with #symfony-cmf and i think this would be important feature for us.
I also think, when you persist your route with /my-route you should also ask the router for that name (or in case of the CMF with an content object with that a route.)
If you use the CmfRoutingBundle dynamic router, you should persist your routes to doctrine. The idea of dynamic here is that they can be created at runtime, e.g. in an admin interface or elsewhere by code. The DynamicRouter loads routes from the database.
If you use PHPCR-ODM, the route name is the repository id, typically something like /cms/routes/my-route and you can generate it with that name. If you have access to the route object loaded from the database, you can also generate from that, rather than the path. If you have to hardcode a path in your application, its an indication that probably a normally configured route would be better.
If you just add a route to the route collection on the fly, you would have to make sure that happens in each request, its only available when you add it. With the CMF ChainRouter you could register your own router that does that, but i would not know of a good use case for that. Either you know the route, then you can configure it in the routing.xml|yml|php config file. Or routes are loaded dynamically, in which point you should use DynamicRouter, potentially with a custom route loader.
I'm trying to learn learn how routing works in Symfony2, and so far everything I've read has examples like this:
blog:
path: /blog/{page}
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
This routes requests to /blog/123 to the AcmeBlogBundle Blog controller's "index" action, and passes the 123 parameter as the "page" parameter to that controller action. If no page parameter is passed, then the page defaults to 1.
That's all well and good, but what if you want to simply have a convention based routing system that passes things through like this:
/{bundle}/{controller}/{action}
So, for a URL like this:
/acme/blog/index
It would then call AcmeBlogBundle Blog controller's "index" action.
No specific routing configuration is necessary, it simply infers the bundle, controller, and action from the URL. So you can continue adding bundles, controllers, and actions, and you don't need to modify the routing configuration. It just works.
If this isn't possible, can you at least infer the controller and action from the URL? E.g., perhaps you need a route that specifically identifies the bundle, but can we get the controller and action from the URL?
I read through the Symfony "The Book" page about routing, and I couldn't figure out a way to do this.
No way. This was considered as bad practice and so it was removed from symfony.
But you should take a look at the #Route annotation, as it simplifies configuring routes in such a nice way. Directly attached to the action, there is no lack between config and code.
I have a Web API project as part of my solution (also containing an MVC4 project) and within the Api project I am trying to post a form to the Values controller Post method (from a view also within the Api project).
Using Html.BeginForm() or Html.BeginForm("Post", "Values") posts to /Values/Post but I need it to go to /api/Values/Post
Any idea which overload or settings I need to post to the correct location?
I can hit all the action methods fine from fiddler (e.g. localhost/api/values).
You would need to use BeginRouteForm as link generation to Web API routes always depends on the route name. Also make sure to supply the route value called httproute as below.
#using (Html.BeginRouteForm("DefaultApi", new { controller="Values", httproute="true" }))
The API controller uses a different route to the default. It's supposed to be consumed from JS (AJAX) rather than a real form post so there's no obvious support for it in HtmlHelpers. Try:
Html.BeginForm("values", "api")
This would trick it into thinking "values" is the action and "api" is the controller. "Post" is inferred from the http method.
I am trying to override a controller that I use in Frontend:
AcmeDemoBundle:DemoController //route is /demo
For the backend, I extended this controller in AdminDemoBundle:
AdminDemoBundle:DemoController //route is /admin/demo
I haven't overriden any methods in the new controller yet, but all the routes from AcmeDemoController go to /admin/demo.
Does anyone know why?
Is there a solution to override frontend controllers for backend purposes?
Basically, by extending the controller, all the routes are redefined with the same name and a different prefix, which means that the routes are being replaced.
There is actually no point at all in extending a controller for the backend, as controllers are meant to be tiny. The logic of the app must be kept in services instead.
Moving the logic to a service allows a better organisation of the code. There is no need to worry about backend/frontend when talking about services. Just, create the logic in a service, and use the methods in either a frontend controller or a backend controller.