I want to access my current route (eg /[user_id]/posts) for both regular pages and APIs, so that I can log it in errors, increase page hit counters, etc.
The only automated way I found to retrieve the current route involves useRouter, but that is only accessible in React components.
I want to avoid hardcoding a route in each of my handlers as that can get out of sync easily.
How can I automate retrieving the current route inside a handler?
It's simply not supported by the framework. There are 2 alternatives I found:
1/ Use another framework such as Nest.js. With Nest, you can access router information in an interceptor. E.g. with the Fastify adapter:
#Injectable()
export class InstrumentationInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
return next.handle().pipe(
tap(() => {
const ctx = context.switchToHttp();
console.log(ctx.getRequest().routerPath);
}),
);
}
}
2/ Create your own controller infrastructure and attach metadata to each controller, e.g.:
export class UserController extends BaseController {
getRoute(): string {
return "/user/:userid";
}
}
Note that you will need to manually keep routes in sync with your file structure. The above can be coded via Decorators too.
3?/ Theoretically you can configure a Custom Server in Nest and use a router that supports retrieving route paths, with a hook for you to retrieve the route (e.g. attach it to the request object during a middleware). If you find yourself doing this though, take a look at 1/.
Related
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 am setting up multiple graph mappings to an OrientDB database in Gremlin server. However, I can't find what to script in Groovy plus what to configure in the configuration yaml file in order to be able to map each authenticated user to a single graph, rather than have all the users validated by the authenticator be able to access everything. Is there any way to achieve this?
Gremlin Server does not provide any features for authorization - only authentication. You would have to build something yourself to handle restricting users to different graphs (or other constraints). That would mean building two things:
A custom ChannelInboundHandlerAdapter to handle authorization - maybe called AuthorizationHandler
A custom Channelizer implementation to wire in your custom authorizer to the pipeline - maybe called AuthorizingChannelizer
The AuthorizationHandler would basically just override Netty's channelRead() method
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof RequestMessage){
RequestMessage requestMessage = (RequestMessage) msg;
// examine contents of RequestMessage to see what is being requested
// e.g. the graph - the user information will be there too but
// depending on the authentication method you're using you might need
// to re-decode it at this time as it doesn't appear that the authenticated
// user is placed on the ChannelHandlerContext for some reason. i made
// a note to change that as it seems helpful and is a simple enough thing
// to do
}
}
For the AuthorizingChannelizer you would basically extend the WebSocketChannelizer and override the configure() method:
#Override
public void configure(ChannelPipeline pipeline) {
super.configure(pipeline);
// add an instance of your `AuthorizingChannelizer` to the end of the
// netty pipeline which will put it after the `AuthenticationHandler`
// but before all the Gremlin processing/execution
pipeline.addLast("authorizier", authorizingChannelizer);
}
Then, in your Gremlin Server config you replace the channelizer setting with the fully qualified name of your AuthorizingChannelizer. Assuming you've placed your jar containing that class in Gremlin Server's path it should create an instance of it at startup.
I would look at the existing "handler" and "channelizer" code for more inspiration on how to make this happen.
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 have an application that relies on third party services. In order to make sure that the application works properly, I want to mock the third party services and make sure that the application is working as expected.
This requires that I am able to configure the mock services before creating the requests. However, I am unable to do so.
Consider the following code:
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
//..
class MyTest extends WebTestCase
{
public function testSignupLink()
{
$container = static::createClient()->getContainer();
// This returns a different instance from the one used by the client request
$service = $container->get('third-party-service');
$service->setErrorState(MockService::SOME_ERROR_STATE);
// ...
// The request creates a new instance of the $service internally which doesn't have the error state that was set above
$client->request('POST', '/abc/1');
}
}
The 'abc' controller relies on a service that I can't access. When I access the service from the container, I get a different instance from the one that is used by the client request.
Is there any way to handle this?
If I correctly understood you, here is what you need:
https://github.com/PolishSymfonyCommunity/SymfonyMockerContainer
I am using Durandal in an asp.net application which is all working well. What I would like to achieve is to put something into the routing of it so if required I can stop the current route and redirect.
The reason for this is I want to permission base some routes where the permissions are stored in a database. So during the routing I want to check the route, use web api accordingly to check if they have access to that route and redirect if so OR use a method on the viewmodel to check this and redirect accordingly. I do use the activate function on the viewmodel, I wondered if the route can be redirected here perhaps?
Has anyone done this before?
EDIT:
Following the great answer below the following is the code I eventually used on a test route to get this working. The web api function HasAccessToRoute part returns a bool:
define(['durandal/http', 'plugins/router', 'knockout', 'durandal/app'], function (http, router, ko, app) {
function viewModel() {
var self = this;
self.canActivate = function () {
return http.ajaxRequest("get", "/api/route/hasaccesstoroute?route=test")
.done(function (result) {
if (!result) {
app.showMessage("Test area cannot be accessed");
}
});
};
}
var model = new viewModel();
return model;
});
Yes, it is possible. Take a look at canActivate here. You can return a promise in your canActivate handler and fetch your authorization profiles asynchronously. Once you have the authorization profile, you can then resolve your canActivate with either true or false, accordingly. This is what we do.
Also, the routes in Durandal are client-side, not server-side. Or are you doing server-side rendering with, say, Razor? If not, then the only time you would be going out to the server, essentially, is to obtain data, usually through a RESTful Web API (although you can do this with action-based routes as well).
This is an important point since canActivate is a client-side handler.