Is it possible to restrict a Symfony 2 route for XHR requests only? I want to declare routes, which are only accessible via AJAX.
I do not want to put some extra lines into each AJAX-specific-actions like that:
if ($request->isXmlHttpRequest()) {
// do something
} else {
// do something else
}
I want to define:
one rule for AJAX requests
one rule for GET/POST requests to the same URL
in order to get around having conditions like above.
I know this question is a bit older but meanwhile a new way to achieve this was introduced in Symfony 2.4.
Matching Expressions
For an ajax restriction it would look like this:
contact:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
condition: "request.isXmlHttpRequest()"
Also possible in Annotation:
/**
* ContactAction
*
* #Route("/contact", name="contact", condition="request.isXmlHttpRequest()")
*/
My advice would be to define your own router service instead of default, which would extend from Symfony\Bundle\FrameworkBundle\Routing\Router, and redefine method resolveParameters() with implementing your own logic for handling additional requirements.
And then, you could do something like this in your routing:
your_route:
pattern: /somepattern
defaults: { somedefaults }
requirements:
_request_type: some_requirement
I'm not sure that you can prevent the request taking place, however you can check for an XHR request in the Controller by checking the current Request
The code would look like this:
if ($request->isXmlHttpRequest()) {
// ...
}
This is not 100% reliable, due to among other things, browser inconsistencies and the possibility of proxy interference. However it is the predominant method of checking for an asynchronous request and is recommended by many. If you are cr
URL Parameter
An alternative would be to add a parameter in your URL to identify the request as asynchronous. This is achieved by adding ?ajax=1 to your URL. Then, check for the parameter with:
$AjaxRequest = $request->getParameter('ajax');
If($AjaxRequest == 1) {
//...
}
Of course, at this point you could also create a specific Route e.g. /ajax/index/.
No, you cannot. It is not depend on which framework you are using, AJAX requests basically are just requests to a server. There is no 100% reliable solution, just "hacks".
What you're looking for does not exist in Symfony routing configuration.
Request::isXmlHttpRequest is even not 100% reliable, and checks HTTP headers put by your JavaScript library :
It works if your JavaScript library set an X-Requested-With HTTP header.
It is known to work with Prototype, Mootools, jQuery.
You can use the requirements for reach the described result.
So, suppose that you're defining routes onto yml format, you have to do something like this
my_route:
pattern: /path/to/route
defaults: { _controller: CompanyBundle:Controller:Action, _format: html }
requirements:
_format: xmlhttp /* not sure about the correct format because
i've never checked about */
And you can, of course, use _method: POST or _method: GET
Related
I want to move my all methods from controller, which are executed by AJAX request, to separate folder. For example create file UserBundle/Ajax/Ajax.php and put in this file, my all AJAX request methods.
Is it the right approach, to separate ajax requests, from normal http requests? I can't find any examples how to do it. It is possible in Symfony? I must extend Symfony\Bundle\FrameworkBundle\Controller\Controller in this Ajax.php file? That will be okay, that will be exist two folders in bundle Ajax and Controller, contains controllers but first for ajax request and second for normal http request?
Do You know any architectural pattern, for this problem?
I don't think there is any problem to do that, only make sure to define the routing path correctly:
An example for annotation routing:
# app/config/routing.yml
app_bundle:
resource: "#AppBundle/Controller"
type: annotation
prefix: /
app_bundle_ajax:
resource: "#AppBundle/Ajax"
type: annotation
prefix: /
I must extend Symfony\Bundle\FrameworkBundle\Controller\Controller in this Ajax.php file?
It's not mandatory, but the Symfony\Bundle\FrameworkBundle\Controller\Controller provide you excellent shortcuts like $this->json(...); since Symfony 3.1
You can create simple AjaxController as a service which will receive the ajax requests. Then you can redirect each of the requests to different Controllers and they will return JsonResponces().
I am a newbie to Symfony 2. I am using Symfony 2.3.
I am using yml files for my routes. I want a certain route to be restricted for Ajax call only. How can I achieve that? I have found one similar question but the solution it suggested is for Symfony 2.4
So please help me how can I achieve it.
Currently I have written conditional code in my controller ajax action something like below, but I do not know how to handle if the call to that route is not through Ajax.
if ($request->isXmlHttpRequest()) {
// Some operations
// Prepare the data array based on the Ajax request.
}
return $this->render('AcmeBundle:Ajax:index.html.twig', array('data' => $data));
I want that this route can be accessed only with Ajax else it should throw some exception like invalid route or redirect to some other page.
Thanks in advance.
I don't know such a way in symfony 2.3 but in the version 2.4, the below routing configuration can be used to identify ajax requests.
ajax_route:
path: /your-ajax-route
defaults: { _controller: AcmeBundle:Ajax:index }
condition: "request.isXmlHttpRequest()"
Annotation version of routing can also be used like the following:
/**
* IndexAction
*
* #Route("/your-ajax-route/", name="ajax_route", condition="request.isXmlHttpRequest()")
*/
All the above is only a temporary solution for understanding the ajax request otherwise the headers can be manipulated and you never can identify if the request is xmlhttp. There is no 100% sure way to check the xml http request.
First and foremost, Headers can be manipulated. so a curl request with bunch of headers can pull an ajax request. I believe you can check the headers with Symfony 2.3
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
$ajax = $request->headers->get('HTTP_X_REQUESTED_WITH');
if($ajax != 'xmlhttprequest'){
throw new \Exception("This is not an ajax request");
}
or you can use $_SERVER['HTTP_X_REQUESTED_WITH'] == "xmlhttprequest"
I heve an embedded controller in my base template. It's a search bar.
For the search bar controller, I have a route "myProject/search".
What I would like is that this route will be taken only when the template where I am embedding the controller (base.html.twig) will call it, and not when i manually put in the browser: "myproject/search".
Any idea on how to do that.
I think, since some time you can't do it:
http://symfony.com/doc/current/book/templating.html#embedding-controllers
quote from the docs:
Even though this controller will only be used internally, you'll need
to create a route that points to the controller
(...)
Since Symfony 2.0.20/2.1.5, the Twig render tag now takes an absolute
url instead of a controller logical path. This fixes an important
security issue (CVE-2012-6431) reported on the official blog. If your
application uses an older version of Symfony or still uses the
previous render tag syntax, you should upgrade as soon as possible.
Anyway, I guess, you can try do it yourself by passing some "secret" argument to search action when you call it from your template. Next in the action you check if the argument was passed to it, and if not you throw 404.
Another way to achieve your goal is use .htaccess file.
You can restrict your route to a certain method by _method option in your routing configuration:
your_rote:
pattern: /myProject/search
defaults: { _controller: YourBundle:YourController:YourAction }
requirements:
_method: POST
Having some issue changing the locale on a symfony 2.1 website.
I can't find a way to be able to change the lang without using the _locale on every routes. I know this is against the fundamental rule, but this content will for example not be indexed by engine as it is member only.
Typically, I would like a simple method to be able to change the locale on the request (BC from version 2.1), or on the session, but can't figure out how to do that smoothly. I also would like to avoid the use of a Listener for that.
config.yml file :
framework:
translator: { fallback: %locale% }
session:
routing.yml file :
route_change_lang:
pattern: /changelang/{newlang}
defaults: { _controller: AcmeCoreBundle:Default:switchLanguage, newlang: en }
requirements:
newlang: en|fr|de
Simple action to update the locale of the router :
public function switchLanguageAction($newlang)
{
$request = $this->getRequest();
$request->setLocale($newlang);
$referer_url = $this->get('request')->headers->get('referer');
if ($referer_url != null) {
return $this->redirect($referer_url);
} else {
return $this->redirect($this->generateUrl('route_home'));
}
}
What is the problem? I guess it is related to the default_locale set in the main config.yml file, but documentation is not really clear, any help/hint appreciated
I've come across the same problem, since we cant' use locales in our urls (seo-issues). Also we use locales like en_US and those are stored in a config outside the direct framework access.
What I did is registering an event listener and hooking into the onKernelRequest event. There I check if locale is set in session, if not, I add it to both, request and session.
This way, the framework keeps on behaving like it did before 2.1
If you need more info on how to do this, comment and I'll edit some exaples in here :-)
Restore the old behavior as explain in https://github.com/symfony/symfony/blob/master/UPGRADE-2.1.md#httpfoundation-1
And use the piece of code of Carlos Granados.
You can also read my another answer https://stackoverflow.com/a/12952999/520114
If you set the locale in the request, this is just used for the current request. The next time that a request is issued, the default_locale will be used. Even if now (2.1) the locale is set in the request instead of the session, "It is also possible to store the locale in the session instead of on a per request basis. If you do this, each subsequent request will have this locale." (from the docs). So, you need to do:
$this->get('session')->set('_locale', $newlang);
How to setup default routing in Symfony2?
In Symfony1 it looked something like this:
homepage:
url: /
param: { module: default, action: index }
default_symfony:
url: /symfony/:action/...
param: { module: default }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/...
I was looking through the cookbook for an answer to this, and think I've found it here. By default, all route parameters have a hidden requirement that they match any character except the / character ([^/]+), but this behaviour can be overridden with the requirements keyword, by forcing it to match any character.
The following should create a default route that catches all others - and as such, should come last in your routing config, as any following routes will never match. To ensure it matches "/" as well, a default value for the url parameter is included.
default_route:
pattern: /{url}
defaults: { _controller: AcmeBundle:Default:index, url: "index" }
requirements:
url: ".+"
I don't think it's possible with the standard routing component.
Take a look to this bundle, it might help :
https://github.com/hidenorigoto/DefaultRouteBundle
// Symfony2 PR10
in routing.yml:
default:
pattern: /{_controller}
It enables you to use this kind of urls: http://localhost/MySuperBundle:MyController:myview
Symfony2 standard routing component does not support it, but this bundle fills the gap Symfony1 left:
https://github.com/LeaseWeb/LswDefaultRoutingBundle
It does what you expect. You can default route a bundle using this syntax:
FosUserBundle:
resource: "#FosUserBundle"
prefix: /
type: default
It scans your bundle and automatically adds routes to your router table that you can debug by executing:
app/console router:debug
Example of automatically added default routes:
[router] Current routes
Name Method Pattern
fos_user.user.login_check ANY /user/login_check.{_format}
fos_user.user.logout ANY /user/logout.{_format}
fos_user.user.login ANY /user/login.{_format}
...
You see that it also supports the automatic "format" selection by using a file extension (html, json or xml).
Here is an example: http://docs.symfony-reloaded.org/master/quick_tour/the_big_picture.html#routing
A route definition has only one mandatory parameter pattern and three optionals parameters defaults, requirements and options.
Here's a route from my own project:
video:
pattern: /watch/{id}/{slug}
defaults: { _controller: SiteBundle:Video:watch }
requirements: { id: "\d+", slug: "[\w-]+"
Alternatively, you can use #Route annotation directly in a controller file. see https://github.com/sensio/SensioFrameworkExtraBundle/blob/master/Resources/doc/annotations/routing.rst
As for the default routes, I think Symfony2 encourages explicit route mapping.
Create a default route is not a good way of programming. Why? Because for this reason was implemented Exception.
Symfony2 is built just to do right things in the right way.
If you want to redirect all "not found" routes you should use exception, like NotFound404 or something similar. You can even customise this page at your own.
One route is for one purpose. Always. Other think is bad.
You could create your own bundle that handled all requests and used URL parameters to construct a string to pass to the controller's forward method. But that's pretty crappy, I'd go with well defined routes, it keeps your URLs cleaner, and decouples the URL and controller names. If you rename a bundle or something, do you then have to refactor your URLs?
If you want to create a "catch all", your best bet would be to hook on the KernelEvents::EXCEPTION event. This event gets triggered whenever an Exception falls through to the HttpKernel, this includes the NotFoundHttpException thrown when the router cannot resolve a route to a Controller.
The effect would be similar to Symfony's stylized 404 page that gets rendered when you send the request through app_dev.php. Instead of returning a 404, you perform whatever logic you're looking to.
It depends... Some of mine look like this:
api_email:
resource: "#MApiBundle/Resources/config/routing_email.yml"
prefix: /
and some look like
api_images:
path: /images/{listingId}/{width}/{fileName}
defaults: { _controller: ApiBundle:Image:view, listingId: null, width: null, fileName: null }
methods: [GET]
requirements:
fileName: .+