I wish to show custom exception pages in my application. In documentation is written:
"Note: We can also use HMVC to issue a sub-request to another page rather than generating the Response in the HTTP_Exception itself." The problem is i don't know how to do it.
Code which I am using is:
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
/**
* Generate a Response for the 404 Exception.
*
* The user should be shown a nice 404 page.
*
* #return Response
*/
public function get_response()
{
$view = View::factory('errors/404');
// Remembering that `$this` is an instance of HTTP_Exception_404
$view->message = $this->getMessage();
$response = Response::factory()
->status(404)
->body($view->render());
return $response;
}
}
It is working but i need to show the message in currently used template.
Regards
You shouldn't need to bother with sub-requests if all you want is to use the template. Here's the easy way:
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
public function get_response()
{
$view = View::factory('errors/404');
// Remembering that `$this` is an instance of HTTP_Exception_404
$view->message = $this->getMessage();
// Wrap it in our layout
$layout = $layout = View::factory('template');
$layout->body = $view->render();
$response = Response::factory()
->status(404)
->body($layout->render());
return $response;
}
}
If you really must use a sub-request, just do the sub-request normally:
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
public function get_response()
{
$request = Request::factory('route_to_error_page_action')
->method(Request::POST)
->post(array('message' => $this->getMessage()));
$response = $request->execute();
// If you didn't return the 404 code from the sub-request, uncomment this:
$response->status(404);
return $response;
}
}
Related
I have a function in my Laravel application that generates TwiML for a holding queue. It seems that when I try to dynamically generate the value for the waitUrl attribute, I end up getting a 500 server error during runtime. Routes are properly established and I'm able to view the correct XML at the waitURL in the browser. However, the error persists.
If I create a static XML file with the same exact content, or use a TwiML Bin, it works like a charm.
Here are the relevant functions:
public function wait() {
return $this->generateWaitTwiml();
}
public function onHold($agentId) {
return $this->generateHoldQueueTwiml($agentId, '/phone/wait');
}
private function generateHoldQueueTwiml($agentId, $waitUrl = null) {
$queue = $agentId . '_hold';
if ($waitUrl === null){
$waitUrl = 'path_to_static.xml';
}
$queue = $agentId . '_hold';
$response = new Twiml();
$response->enqueue(
$queue,
['waitUrl' => $waitUrl]
);
return response($response)->header('Content-Type', 'application/xml');
}
private function generateWaitTwiml() {
$response = new Twiml();
$response
->play('http://path_to_my.mp3');
return response($response)->header('Content-Type', 'application/xml');
}
This was resolved by excluding the URIs from the CSRF verification (in VerifyCsrfToken.php):
class VerifyCsrfToken extends Middleware {
protected $except = [
'uri/',
'uri2/*',
];
}
I try to log if user type wrong url parameter for a route with
'constraints' => array('personalnumber' => '[0-9]*')
$error = $e->getError();
if ($error == Application::ERROR_ROUTER_NO_MATCH) {
$url = $e->getRequest()->getUriString();
$sm->get('Zend\Log\RouteLogger')->warn('Url could not match to routing: ' . $url);
}
Can I get a specific error like: Value for Parameter "id" must type integer?
That won't be so easy. You would have to build your own functionality to find out the exact details on why the route didn't match.
Route matching is checked using the RouteInterface::match method from the corresponding class. For example for segment routes this method can be found in the Zend\Router\Http\Segment class on line 359-404.
If there is no match, the class returns null/void. Details on why the route didn't match is not part of the response, so you would have to do such in depth analysis yourself and write your own custom error response.
Such a solution could be to do manually validate the person number (for example by isolating it from the request url) when the dispatch error event is triggered and return your own custom response before the default 404 response.
<?php
namespace Application;
use Zend\Http\Response;
use Zend\Mvc\MvcEvent;
use Zend\Router\Http\RouteMatch;
class Module{
public function onBootstrap(MvcEvent $event)
{
$eventManager = $event->getApplication()->getEventManager();
$eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR, array($this, 'validatePersonNumber'), 1000);
}
public function validatePersonNumber(MvcEvent $event)
{
if ($event->getError() !== Application::ERROR_ROUTER_NO_MATCH) {
// Not a 404 error
return;
}
$request = $event->getRequest();
$controller = $event->getController();
if($controller !== 'Application\Expected\ControllerName'){
// not a controller for person number route
return;
}
$url = $request->getRequestUri();
$personNumber = ''; //...manually isolate the person number from the request...
/** #var Response $response */
$response = $event->getResponse();
$response->setStatusCode(404);
$viewModel = $event->getViewModel();
$viewModel->setTemplate('error/404');
$event->setViewModel($viewModel);
$event->stopPropagation(true);
if (strlen($personNumber) !== 12) {
$viewModel->setVariable('message', 'A person number should have 12 characters');
}
if(...additional check...){
$viewModel->setVariable('message', 'Some other message');
}
}
}
To make things prettier you could consider moving all this into a Listener class (instead of polluting your module.php file) and you could also consider the 404 code here. Most likely there is a more suitable status code for such validation response.
Note: This is not a completely finished example, it needs more work!
I have some custom routes
Route::get('/files/', [
'as' => 'read',
'uses' => 'Myname\MyPlugin\Http\Controllers\FilesController#read'
]);
Somewhere in my class I have a function to validate a path
private function getPath()
{
$path = Input::get('path');
if (!$path)
{
throw new MyException('parameter is missing. path required', 400);
}
return base_path().'/'.$path;
}
I have set a custom error handler with a JSOM but it's the error handler of OctoberCMS that render the error in HTML format.
Do you know a way to replace default error handler of OctoberCMS by a custom one ?
Thanks
Just found the anwser in the documentation : https://octobercms.com/docs/services/error-log#exception-handling
October provide App:error to manage Exception in your plugin.
App::error(function(MyException $exception) {
//do what you want here
}
Don't forger to create a custom Exception for your plugin. If you use the generic Exception, you'll catch all the Exceptions.
I am posting an alternative solution here for a custom error handler because of this questions's visibility on Google.
I had problems using App:error due to this issue on Github: https://github.com/octobercms/october/issues/3416
Rather than using October's App::error for a custom error handling, try the following:
Create a custom error handler that inherit's from October's error handler. For example, create the following class in plugins/{AUTHOR}/{PLUGIN}/classes/CustomHandler.php (assuming you are developing a plugin for OctoberCMS). Override the render method of the handler.
<?php
namespace {AUTHOR}\{PLUGIN}\Classes; //<-- CHANGE ME OBVIOUSLY FOR YOUR PLUGIN
use October\Rain\Foundation\Exception\Handler;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Response;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException; // As an example exception you want to handle ...
/* Custom error handler which replaces the default error handler for OctoberCMS. */
class CustomHandler extends Handler
{
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
/* Custom JSON response for ModelNotFoundException exceptions. */
if($exception instanceof ModelNotFoundException){
return Response::json(['error' => 'A record was not found for the resource that was requested.'], 404);
}
/* The rest of this code is just the 'default' code from OctoberCMS' error handler. */
/* The only change is the code above this comment where I handle a specific exception in a unique way.*/
/* i.e. I decided to return JSON for the error rather than an HTML error page (in debug mode). */
if (!class_exists('Event')) {
return parent::render($request, $exception);
}
$statusCode = $this->getStatusCode($exception);
$response = $this->callCustomHandlers($exception);
if (!is_null($response)) {
return Response::make($response, $statusCode);
}
if ($event = Event::fire('exception.beforeRender', [$exception, $statusCode, $request], true)) {
return Response::make($event, $statusCode);
}
return parent::render($request, $exception);
}
}
Then, in your plugin registration file Plugin.php, add a boot method with the following code:
<?php namespace {AUTHOR}\{PLUGIN};
use System\Classes\PluginBase;
use {AUTHOR}\{PLUGIN}\Classes\CustomHandler; //<-- IMPORTANT
use Illuminate\Contracts\Debug\ExceptionHandler; //<-- IMPORTANT
class Plugin extends PluginBase
{
/**
* #var array Plugin dependencies
*/
public $require = [];
public function registerComponents()
{
}
public function registerSettings()
{
}
public function boot(){
/* Replace the default error handler of OctoberCMS to return JSON format responses instead. */
/* Also, this is used in order to control the responses for certain types of errors/exceptions. */
/* We are going about it this way because App::error (as mentioned in the documentation for OctoberCMS), */
/* was not returning the response properly presumably because of a bug. Argh... */
$this->app->bind(
ExceptionHandler::class,
CustomHandler::class
);
}
}
Since, the new CustomHandler.php was added to the classes directory, the new class should already be picked up. No need for composer dump-autoload -o or changes to the composer.json.
That is it. When the ModelNotFoundException occurs, the response given to the web browser will be JSON that you wrote (from the custom error handler we created).
Relevant links:
https://blog.sarav.co/registering-custom-exception-handler-laravel-5/
https://laravel.com/docs/5.8/container#binding
https://octobercms.com/forum/post/is-it-posible-to-bring-back-the-app-directory-for-native-laravel
I'm trying to create a soap server in laravel 5.2. This is my code:
Content of SoapController.php:
<?php namespace Giant\Http\Controllers;
class SoapController extends Controller {
public function __construct() {
parent::__construct();
ini_set('soap.wsdl_cache_enabled', 0);
ini_set('soap.wsdl_cache_ttl', 0);
ini_set('default_socket_timeout', 300);
ini_set('max_execution_time', 0);
}
public function server() {
$location = url('server'); // http://payment.dev/server
$namespace = $location;
$class = "\\Giant\\Http\\Controllers\\HelloWorld";
$wsdl = new \WSDL\WSDLCreator($class, $location);
$wsdl->setNamespace($namespace);
if (isset($_GET['wsdl'])) {
$wsdl->renderWSDL();
exit;
}
$wsdl->renderWSDLService();
$wsdlUrl = url('wsdl/server.wsdl');
$server = new \SoapServer(
url('server?wsdl'),
array(
'exceptions' => 1,
'trace' => 1,
)
);
$server->setClass($class);
$server->handle();
exit;
}
public function client() {
$wsdl = url('server?wsdl');
$client = new \SoapClient($wsdl);
try {
$res = $client->hello('world');
dd($res);
} catch (\Exception $ex) {
dd($ex);
}
}
}
class HelloWorld {
/**
* #WebMethod
* #desc Hello Web-Service
* #param string $name
* #return string $helloMessage
*/
public function hello($name) {
return "hello {$name}";
}
}
My wsdl file is: wsdl
And my routes:
Route::any('/server', 'SoapController#server');
Route::any('/client', 'SoapController#client');
And the result I get:
Internal Server Error
:(
I use piotrooo/wsdl-creator to generate wsdl. (There is no problem with that, It is working in laravel 4.2). And I have also tried nusoap and php2wsdl libraries.
My SoapClient is working well. Because it can get service from other soap servers in other urls, But I think my SoapServer can not work well.
I even get no errors in error-log file.
I just figured out wht was the problem:
The problem with log was that i was checking error-log in my www folder while laravel has its own log file. And using that i figured that i have problem with TokenMismatchException. Laravel's CsrfVerifyMiddleware would not letting me to request using soap.
I just added my url to "except" array inside CsrfVerifyMiddleware file.
Do not use two classes in one file
This is my experience from our project in which used Soap
This is SoapServerController . Paste wsdl file in root folder of your project
class SoapServerController extends Controller {
public function service() {
$server = new \SoapServer('http://' . request()->server('HTTP_HOST') . '/yourwsdlfile.wsdl');
$server->setClass('App\Http\Requests\somenamespace\SoapRequest');
$server->handle();
}
}
and in requests create class for requests like this:
class SoapRequest{
public function functionFromWsdl($args if you want) {
$parameters = (array) $args;
return with(new fooClass())->barMethod($parameters);
}
}
and route must be post:
Route::post('webservice','SoapServerController#service');
In laravel 5 all before statements have turned into middlewares (just like what is in django framework). And you need to implement using middlewares.
I'm having a small problem when trying to flash a message and redirect the user back to the previous page in Symfony 2.
I have a very simple CRUD. When new, or edit, i want to flash a message if something goes wrong in the respective create/update methods:
User --GET--> new
new --POST--> create (fails)
--REDIRECT--> new (with flash message)
I'm doing the following:
$this->container->get('session')->setFlash('error', 'myerror');
$referer = $this->getRequest()->headers->get('referer');
return new RedirectResponse($referer);
However, it's not redirecting to the correct referrer! Even though the value of referrer is correct (eg.: http://localhost/demo/2/edit/) It redirects to the index. Why?
This is an alternative version of Naitsirch and Santi their code. I realized a trait would be perfect for this functionality. Also optimized the code somewhat. I preferred to give back all the parameters including the slugs, because you might need those when generating the route.
This code runs on PHP 5.4.0 and up. You can use the trait for multiple controllers of course. If you put the trait in a seperate file make sure you name it the same as the trait, following PSR-0.
<?php
trait Referer {
private function getRefererParams() {
$request = $this->getRequest();
$referer = $request->headers->get('referer');
$baseUrl = $request->getBaseUrl();
$lastPath = substr($referer, strpos($referer, $baseUrl) + strlen($baseUrl));
return $this->get('router')->getMatcher()->match($lastPath);
}
}
class MyController extends Controller {
use Referer;
public function MyAction() {
$params = $this->getRefererParams();
return $this->redirect($this->generateUrl(
$params['_route'],
[
'slug' => $params['slug']
]
));
}
}
For symfony 3.0,flash message with redirection back to previous page,this can be done in controller.
$request->getSession()
->getFlashBag()
->add('notice', 'success');
$referer = $request->headers->get('referer');
return $this->redirect($referer);
The message from Naitsirch presented in the next url:
https://github.com/symfony/symfony/issues/2951
Seems a good solution for that you need:
public function getRefererRoute()
{
$request = $this->getRequest();
//look for the referer route
$referer = $request->headers->get('referer');
$lastPath = substr($referer, strpos($referer, $request->getBaseUrl()));
$lastPath = str_replace($request->getBaseUrl(), '', $lastPath);
$matcher = $this->get('router')->getMatcher();
$parameters = $matcher->match($lastPath);
$route = $parameters['_route'];
return $route;
}
Then with a redirect:
public function yourFunctionAction()
{
$ruta = $this->getRefererRoute();
$locale = $request->get('_locale');
$url = $this->get('router')->generate($ruta, array('_locale' => $locale));
$this->getRequest()->getSession()->setFlash('notice', "your_message");
return $this->redirect($url);
}
This works for me:
$this->redirect($request->server->get('HTTP_REFERER'));
I have similar functionality on my site. It is multilingual. Articles exists only in a single locale. When the user will try to switch to other locale, it should redirect back to previous page and flash message that that page/article doesn't exist on the requested locale.
/en/article/3 -> /fr/article/3 (404) -> Redirect(/en/article/3)
Here is my version of the script that works well on dev and prod environments:
$referer = $request->headers->get('referer')
// 'https://your-domain.com' or 'https://your-domain.com/app_dev.php'
$base = $request->getSchemeAndHttpHost() . $request->getBaseUrl();
// '/en/article/3'
$path = preg_replace('/^'. preg_quote($base, '/') .'/', '', $referer);
if ($path === $referer) {
// nothing was replaced. referer is an external site
} elseif ($path === $request->getPathInfo()) {
// current page and referer are the same (prevent redirect loop)
} else {
try {
// if this will throw an exception then the route doesn't exist
$this->container->get('router')->match(
// '/en/hello?foo=bar' -> '/en/hello'
preg_replace('/\?.*$/', '', $path)
);
// '/app_dev.php' . '/en/article/3'
$redirectUrl = $request->getBaseUrl() . $path;
return new RedirectResponse($redirectUrl);
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {}
}
I just set up a simple app, and it seems to work fine. My createAction() looks like this:
public function createAction()
{
$entity = new Pokemon();
$request = $this->getRequest();
$form = $this->createForm(new PokemonType(), $entity);
$form->bindRequest($request);
if ($entity->getName() == "pikachu")
{
$this->container->get("session")->setFlash("error", "Pikachu is not allowed");
$url = $this->getRequest()->headers->get("referer");
return new RedirectResponse($url);
}
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('pokemon_show', array('id' => $entity->getId())));
}
return $this->render('BulbasaurBundle:Pokemon:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView()
));
}
The flow goes:
User navigates to /new
User enters invalid option of "pikachu"
User clicks submit (POSTs to /create)
Application rejects the entry, adds flash message, and redirects back to /new
User sees /new with the flash message
A few things to check:
Is your route for /demo/{entityId}/edit actually working? (i.e. if you enter it in the browser, does it actually go to where you expect it to?)
Are you chaining together different redirects/forwards? I've noticed that I get unexpected (but correct) behavior when I have a controller that redirects to a URL, and the controller responsible for that URL also redirects somewhere else. I've fixed this issue by using forwards instead.
That said, if all else fails, you could just use the controller's redirect() method to manage the redirect:
public function createAction()
{
...
return $this->redirect($this->generateUrl("pokemon_new"));
...
}
Here you go, declare this as a service and it will return referer to you wherever and whenever you need it. No traits, no weird dependencies.
class Referer
{
/** #var RequestStack */
private $requestStack;
/** #var RouterInterface */
private $router;
public function __construct(RequestStack $requestStack, RouterInterface $router)
{
$this->requestStack = $requestStack;
$this->router = $router;
}
public function getReferer() : string
{
$request = $this->requestStack->getMasterRequest();
if (null === $request)
{
return '';
}
//if you're happy with URI (and most times you are), just return it
$uri = (string)$request->headers->get('referer');
//but if you want to return route, here you go
try
{
$routeMatch = $this->router->match($uri);
}
catch (ResourceNotFoundException $e)
{
return '';
}
$route = $routeMatch['_route'];
return $route;
}
}
seems like you need to have a payload for your redirect to point to. it seems like obscure concept code to me. I would also advise you to make sure your configuration files point to the correct redirect code snippet. Check your server access file to make sure it has redirects enabled also.