We have files behind authentication, and I want to do different things for post-authentication redirect if the user entered the application using a URL of a file versus a URL of an HTML resource.
I have a URL: https://subdomain.domain.com/resource/45/identifiers/567/here/11abdf51e3d7-some%20file%20name.png/download. I want to get the route name for this URL.
app/console router:debug outputs this: _route_name GET ANY subdomain.domain.{tld} /resource/{id2}/identifiers/{id2}/here/{id3}/download.
Symfony has a Routing component (http://symfony.com/doc/current/book/routing.html), and I'm trying to call match() on an instance of Symfony\Bundle\FrameworkBundle\Routing\Router as provided by Symfony IOC. I have tried with with the domain and without the domain, but they both create a MethodNotAllowed exception because the route cannot be found. How can I match this URL to a route?
Maybe a bit late but as I was facing the same problem, what I come to is something like
$request = Request::create($targetPath, Request::METHOD_GET, [], [], [], $_SERVER);
try {
$matches = $router->matchRequest($request);
} catch (\Exception $e) {
// throw a 400
}
The key part is to use $_SERVER superglobal array in order to have all things setted straight away.
According to this, Symfony uses current request's HTTP method while matching. I guess your controller serves POST request, while your download links are GET.
The route name is available in the _route_name attribute of the Request object: $request->attributes->get('_route_name').
You can do something like this ton get the route name:
public/protected/private function getRefererRoute(Request $request = null)
{
if ($request == null)
$request = $this->getRequest();
//look for the referer route
$referer = $request->headers->get('referer');
$path = substr($referer, strpos($referer, $request->getBaseUrl()));
$path = str_replace($request->getBaseUrl(), '', $lastPath);
$matcher = $this->get('router')->getMatcher();
$parameters = $matcher->match($path);
$route = $parameters['_route'];
return $route;
}
EDIT:
I forgot to explain what I was doing. So basicly you are getting the page url ($referer) then taking out your website's base url with str_replace and then trying to match the remaining part of the path with a know route pattern using route matcher.
EDIT2:
Obviously you need to have this inside you controller if you want to be able to use $this->get(...)
Related
for example I was on the home page, it has the route name "home", after I left on another page for example edit, how to get the name of the previous route on the edit page (here it is home)? thanks
you can get the referrer and try to "match" it with the routing service to see if a route exists for this path:
$referer = $request->headers->get('referer'); // get the referer, it can be empty!
if (!\is_string($referer) || !$referer) {
echo 'Referer is invalid or empty.';
return;
}
$refererPathInfo = Request::create($referer)->getPathInfo();
// try to match the path with the application routing
$routeInfos = $this->router->match($refererPathInfo);
// get the Symfony route name if it exists
$refererRoute = $routeInfos['_route'] ?? '';
Check out the full snippet on my blog.
Anyone knows why the PUT method doesn't work using PHP Symfony?
If I replace PUT to POST everything works fine
/**
* #Route("/api/product/update", name="product_udpate", methods = {"PUT"})
*/
i am reading variables like that
$request = Request::createFromGlobals();
echo $request->request->get('name');
error:
No route found for "PUT /api/product/update/23" (404 Not Found)
The problem is you are not creating the route correctly. Basically, you need to add the "id" to the route.
/**
* #Route("/api/product/update/{id}", name="product_udpate", methods = {"PUT"})
*/
public function updateAction(Request $request, $id)
{
// Your logic here
$name = $request->get('name');
}
You got the following error because you have not configured route correctly.
error: No route found for "PUT /api/product/update/23" (404 Not Found)
If you want to add id along with your desire url, you have to define in your route.
Thus, you can update your route:
/**
* #Route("/api/product/update/{id}", name="product_udpate", methods = {"PUT"}, defaults={"id"=null})
*/
As stated in the symfony documentation How to Use HTTP Methods beyond GET and POST in Routes
Unfortunately, life isn't quite this simple, since most browsers do not support sending PUT and DELETE requests via the method attribute in an HTML form. Fortunately, Symfony provides you with a simple way of working around this limitation. By including a _method parameter in the query string or parameters of an HTTP request, Symfony will use this as the method when matching routes
So you have to fake the method like this one :
<form action='your route'>
<input type='hidden' name='_method' value='PUT'>
//do something.......
</form>
How can I redirect to an external URL within a symfony action?
I tried this options :
1- return $this->redirect("www.example.com");
Error : No route found for "GET /www.example.com"
2- $this->redirect("www.example.com");
Error : The controller must return a response (null given).
3- $response = new Response();
$response->headers->set("Location","www.example.com");
return $response
No Error but blank page !
Answer to your question is in official Symfony book.
http://symfony.com/doc/current/book/controller.html#redirecting
public function indexAction()
{
return $this->redirect('http://stackoverflow.com');
// return $this->redirect('http://stackoverflow.com', 301); - for changing HTTP status code from 302 Found to 301 Moved Permanently
}
What is the "URL"? Do you have really defined route for this pattern? If not, then not found error is absolutelly correct. If you want to redirect to external site, always use absolute URL format.
You have to use RedirectResponse instead of Response
use Symfony\Component\HttpFoundation\RedirectResponse;
And then:
return new RedirectResponse('http://your.location.com');
I am hitting RestfulServer via an ajax call (url: BaseHref + "api/v1/Post/" + postId + '/PostTracks' to retrieve DataObject relations:
public function PostTracks(){
$controller = Controller::curr();
$request = $controller->getRequest();
$passkey = $request->getHeader('passkey');
$tracks = $this->owner->Tracks();
$set = array();
foreach($tracks as $track)
{
$set[] = array(
'm4aURL' => $track->m4a()->URL,
'oggURL' => $track->ogg()->URL,
'Title' => $track->Title
);
}
$this->outputJSON(200, $set);
}
At the top of the method I am trying to grab the value of a custom header that I sent in my ajax call via the beforeSend method. I have verified that the header is sent in the request to RestfulServer controller, but am having trouble getting the value.I am not getting anything for the value of $passkey.
How can I get header info from a RestfulServer controller. I don't understand why getRequest isn't working since RestfulServer extends from Controller.
You can use print_r($request->getHeaders()) to see all the headers attached to the request. In any case, I suspect the issue is with the casing of "passkey". By default SilverStripe will parse header names in CamelCaseFormat - so I suspect the header will be called Passkey or PassKey.
One nice way to debug issues with request is using Debug::dump($request->getHeaders()) or Debug::log($request->getHeaders()).
The latter will write a log file to the site that you can then track if you have terminal access to the server by "tail -f debug.log", or downloading them again and again.
That way you can see what logs out when you cant drirectly access the url.
I'm trying to send an email from a ContainerAwareCommand in Symfony2. But I get this exception when the email template is render by:
$body = $this->templating->render($template, $data);
Exception:
("You cannot create a service ("templating.helper.assets") of an inactive scope ("request").")
I found in github that this helper need the request object. Anybody knows how can I to instance the Request object?
You need to set the container into the right scope and give it a (fake) request. In most cases this will be enough:
//before you render template add bellow code
$this->getContainer()->enterScope('request');
$this->getContainer()->set('request', new Request(), 'request');
The full story is here. If you want to know the details read this issue on github.
The problem arises because you use asset() function in your template.
By default, asset() relies on Request service to generate urls to your assets (it needs to know what is the base path to you web site or what is the domain name if you use absolute asset urls, for example).
But when you run your application from command line there is no Request.
One way to fix this it to explicitely define base urls to your assets in config.yml like this:
framework:
templating:
assets_base_urls: { http: ["http://yoursite.com"], ssl: ["http://yoursite.com"] }
It is important to define both http and ssl, because if you omit one of them asset() will still depend on Request service.
The (possible) downside is that all urls to assets will now be absolute.
Since you don't have a request, you need to call the templating service directly like this:
$this->container->get('templating')->render($template, $data);
Following BetaRide's answer put me on the right track but that wasn't sufficient. Then it was complaining: "Unable to generate a URL for the named route "" as such route does not exist."
To create a valid request I've modified it to request the root of the project like so:
$request = new Request();
$request->create('/');
$this->container->enterScope('request');
$this->container->set('request', $request, 'request');
You might need to call a different route (secured root?), root worked for me just fine.
Symfony2 Docs
Bonus addition:
I had to do so much templating/routing in cli through Symfony2 commands that I've updated the initializeContainer() method in AppKernel. It creates a route to the root of the site, sets the router context and fakes a user login:
protected function initializeContainer()
{
parent::initializeContainer();
if (PHP_SAPI == 'cli') {
$container = $this->getContainer();
/**
* Fake request to home page for cli router.
* Need to set router base url to request uri because when request object
* is created it perceives the "/portal" part as path info only, not base
* url and thus router will not include it in the generated url's.
*/
$request = Request::create($container->getParameter('domain'));
$container->enterScope('request');
$container->set('request', $request, 'request');
$context = new RequestContext();
$context->fromRequest($request);
$container->get('router')->setContext($context);
$container->get('router')->getContext()->setBaseUrl($request->getRequestUri());
/**
* Fake admin user login for cli. Try database read,
* gracefully print error message if failed and continue.
* Continue mainly for doctrine:fixture:load when db still empty.
*/
try {
$user = $container->get('fos_user.user_manager')->findUserByUsername('admin');
if ($user !== null) {
$token = $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->getContainer()->get('security.token_storage')->setToken($token);
}
} catch (\Exception $e) {
echo "Fake Admin user login failed.\n";
}
}
}
You might not need the last $container->get('router')->getContext()->setBaseUrl($request->getRequestUri()); part, but I had to do it because my site root was at domain.com/siteroot/ and the router was stripping /siteroot/ away for url generation.