53

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.

Carlos Granados
  • 11,273
  • 1
  • 38
  • 44
Chris
  • 8,031
  • 10
  • 41
  • 67
  • Why? The data they expose won't be available to anyone who couldn't get at it anyway. – Quentin Jul 19 '12 at 10:29
  • Why would you want to do that? – Elnur Abdurrakhimov Jul 19 '12 at 10:30
  • Want to restrict access to the route only for XHR requests. – Chris Jul 19 '12 at 10:38
  • 6
    @crudolf — If someone (or two people) ask you **why** you want to do X, then just repeating that you want to do X is not helpful. – Quentin Jul 19 '12 at 12:53
  • 1
    @Quentin -- thanks. I have added a reason, why I am asking the question. – Chris Jul 19 '12 at 12:58
  • @crudolf — My question was "Why are you trying to restrict access?" not "Why use routes to do it?" – Quentin Jul 19 '12 at 13:02
  • @Quentin -- I have updated the question. I guess you will not be confident. The application needs to behave differently for AJAX and general a browser request. It is obviously not for security reasons. – Chris Jul 19 '12 at 13:18
  • @Quentin -- the question was "is it possible.." - so if it's not directly supported from the framework, it's also an answer. – Chris Jul 19 '12 at 13:19
  • @crudolf "It is obviously not for security reasons" Would this be a bad thing to use this for security reasons (so that only the same domain JS app could access the route)? – Silverspur Feb 11 '15 at 21:36
  • JQuery HTTP headers can be spoofed, so there is no security gain by adding those rules. – Chris Feb 12 '15 at 12:42

6 Answers6

103

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()")
 */
Markus Kottländer
  • 8,228
  • 4
  • 37
  • 61
10

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
Vitalii Zurian
  • 17,858
  • 4
  • 64
  • 81
  • This sounds really good. How performance relevant is it? Is the Default Routing Service really that Poorly implemented? I think i give it a try. – Chris Jul 26 '12 at 07:12
  • 1
    Well... Poorly or not, it is implemented :) And the best thing about Symfony2 is that you can redefine services and extend basic controllers with whatever functionality ;) – Vitalii Zurian Jul 26 '12 at 07:24
3

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/.

Rich
  • 892
  • 1
  • 9
  • 17
2

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".

dmirkitanov
  • 1,396
  • 1
  • 12
  • 29
  • On the server side of a specific application i know which client side JavaScript Library is being used. Therefore it is not a hack, if i require a specific http header field for specific routes. – Chris Jul 28 '12 at 08:46
1

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.

AlterPHP
  • 12,667
  • 5
  • 49
  • 54
  • 1
    If I know, that my cilent side library is a specific one, then this method will be 100% reliable. – Chris Jul 28 '12 at 13:13
0

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

DonCallisto
  • 29,419
  • 9
  • 72
  • 100
  • This is what I am searching for. But what is the concrete value name? – Chris Jul 23 '12 at 07:55
  • @crudolf documentation about is very, very, poor. I suggest to start with the parameter i suggest you and, then, to try something like "xml" or "ajax" directly. Now I'am not currently onto a machine that permitt me to do this kind of try. – DonCallisto Jul 23 '12 at 08:09