[SoapBundle] Enhanced exception management with Symfony2
This commit is contained in:
parent
10705aa9e2
commit
6a6661012e
|
@ -25,6 +25,7 @@
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"ass/xmlsecurity": "~1.0",
|
"ass/xmlsecurity": "~1.0",
|
||||||
"symfony/framework-bundle": "~2.0",
|
"symfony/framework-bundle": "~2.0",
|
||||||
|
"symfony/twig-bundle": "~2.0",
|
||||||
"zendframework/zend-mail": "2.1.*",
|
"zendframework/zend-mail": "2.1.*",
|
||||||
"zendframework/zend-mime": "2.1.*",
|
"zendframework/zend-mime": "2.1.*",
|
||||||
"zendframework/zend-soap": "2.1.*"
|
"zendframework/zend-soap": "2.1.*"
|
||||||
|
|
|
@ -12,14 +12,18 @@
|
||||||
|
|
||||||
namespace BeSimple\SoapBundle\Controller;
|
namespace BeSimple\SoapBundle\Controller;
|
||||||
|
|
||||||
|
use BeSimple\SoapBundle\Handler\ExceptionHandler;
|
||||||
use BeSimple\SoapBundle\Soap\SoapRequest;
|
use BeSimple\SoapBundle\Soap\SoapRequest;
|
||||||
use BeSimple\SoapBundle\Soap\SoapResponse;
|
use BeSimple\SoapBundle\Soap\SoapResponse;
|
||||||
use BeSimple\SoapServer\Exception as SoapException;
|
use BeSimple\SoapServer\Exception as SoapException;
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||||
|
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Kerl <christian-kerl@web.de>
|
* @author Christian Kerl <christian-kerl@web.de>
|
||||||
|
@ -100,6 +104,46 @@ class SoapWebServiceController extends ContainerAware
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an Exception to a SoapFault Response.
|
||||||
|
*
|
||||||
|
* @param Request $request The request
|
||||||
|
* @param FlattenException $exception A FlattenException instance
|
||||||
|
* @param DebugLoggerInterface $logger A DebugLoggerInterface instance
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
* @throws \LogicException When the request query parameter "_besimple_soap_webservice" does not exist
|
||||||
|
*/
|
||||||
|
public function exceptionAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
|
||||||
|
{
|
||||||
|
if (!$webservice = $request->query->get('_besimple_soap_webservice')) {
|
||||||
|
throw new \LogicException(sprintf('The parameter "%s" is required in Request::$query parameter bag to generate the SoapFault.', '_besimple_soap_webservice'), null, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$view = 'TwigBundle:Exception:'.($this->container->get('kernel')->isDebug() ? 'exception' : 'error').'.txt.twig';
|
||||||
|
$code = $exception->getStatusCode();
|
||||||
|
$details = $this->container->get('templating')->render($view, array(
|
||||||
|
'status_code' => $code,
|
||||||
|
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
|
||||||
|
'exception' => $exception,
|
||||||
|
'logger' => $logger,
|
||||||
|
));
|
||||||
|
|
||||||
|
$server = $this
|
||||||
|
->container
|
||||||
|
->get(sprintf('besimple.soap.context.%s', $webservice))
|
||||||
|
->getServerBuilder()
|
||||||
|
->withHandler(new ExceptionHandler($exception, $details))
|
||||||
|
->build()
|
||||||
|
;
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$server->handle($request->getContent());
|
||||||
|
|
||||||
|
return new Response(ob_get_clean());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method gets called once for every SOAP header the \SoapServer received
|
* This method gets called once for every SOAP header the \SoapServer received
|
||||||
* and afterwards once for the called SOAP operation.
|
* and afterwards once for the called SOAP operation.
|
||||||
|
@ -125,13 +169,7 @@ class SoapWebServiceController extends ContainerAware
|
||||||
);
|
);
|
||||||
|
|
||||||
// forward to controller
|
// forward to controller
|
||||||
try {
|
$response = $this->container->get('http_kernel')->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false);
|
||||||
$response = $this->container->get('http_kernel')->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->soapResponse = new Response(null, 500);
|
|
||||||
|
|
||||||
throw $e instanceof \SoapFault || $this->container->getParameter('kernel.debug') ? $e : new SoapException\ReceiverSoapFault($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->setResponse($response);
|
$this->setResponse($response);
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,8 @@ class BeSimpleSoapExtension extends Extension
|
||||||
$serviceConfig['name'] = $name;
|
$serviceConfig['name'] = $name;
|
||||||
$this->createWebServiceContext($serviceConfig, $container);
|
$this->createWebServiceContext($serviceConfig, $container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$container->setParameter('besimple.soap.exception_listener.controller', $config['exception_controller']);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
||||||
|
|
|
@ -40,6 +40,12 @@ class Configuration
|
||||||
$this->addServicesSection($rootNode);
|
$this->addServicesSection($rootNode);
|
||||||
$this->addWsdlDumperSection($rootNode);
|
$this->addWsdlDumperSection($rootNode);
|
||||||
|
|
||||||
|
$rootNode
|
||||||
|
->children()
|
||||||
|
->scalarNode('exception_controller')->defaultValue('BeSimpleSoapBundle:SoapWebService:exception')->end()
|
||||||
|
->end()
|
||||||
|
;
|
||||||
|
|
||||||
return $treeBuilder->buildTree();
|
return $treeBuilder->buildTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the BeSimpleSoapBundle.
|
||||||
|
*
|
||||||
|
* (c) Christian Kerl <christian-kerl@web.de>
|
||||||
|
* (c) Francis Besset <francis.besset@gmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace BeSimple\SoapBundle\EventListener;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||||
|
use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
|
||||||
|
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||||
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Francis Besset <francis.besset@gmail.com>
|
||||||
|
*/
|
||||||
|
class SoapExceptionListener extends ExceptionListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ContainerInterface
|
||||||
|
*/
|
||||||
|
protected $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To avoid conflict between , the logger param is not typed:
|
||||||
|
* The parent class needs and instance of `Psr\Log\LoggerInterface` from Symfony 2.2,
|
||||||
|
* before logger is an instance of `Symfony\Component\HttpKernel\Log\LoggerInterface`.
|
||||||
|
*
|
||||||
|
* @param ContainerInterface $container A ContainerInterface instance
|
||||||
|
* @param string $controller The controller name to call
|
||||||
|
* @param LoggerInterface $logger A logger instance
|
||||||
|
*/
|
||||||
|
public function __construct(ContainerInterface $container, $controller, $logger)
|
||||||
|
{
|
||||||
|
parent::__construct($controller, $logger);
|
||||||
|
|
||||||
|
$this->container = $container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onKernelException(GetResponseForExceptionEvent $event)
|
||||||
|
{
|
||||||
|
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = $event->getRequest();
|
||||||
|
if ('soap' !== $request->getRequestFormat()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attributes = $request->attributes;
|
||||||
|
if (!$webservice = $attributes->get('webservice')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->container->has(sprintf('besimple.soap.context.%s', $webservice))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hack to retrieve the current WebService name in the controller
|
||||||
|
$request->query->set('_besimple_soap_webservice', $webservice);
|
||||||
|
|
||||||
|
parent::onKernelException($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
KernelEvents::EXCEPTION => array('onKernelException', -64),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the BeSimpleSoapBundle.
|
||||||
|
*
|
||||||
|
* (c) Christian Kerl <christian-kerl@web.de>
|
||||||
|
* (c) Francis Besset <francis.besset@gmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace BeSimple\SoapBundle\Handler;
|
||||||
|
|
||||||
|
use BeSimple\SoapServer\Exception\ReceiverSoapFault;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Francis Besset <francis.besset@gmail.com>
|
||||||
|
*/
|
||||||
|
class ExceptionHandler
|
||||||
|
{
|
||||||
|
protected $exception;
|
||||||
|
protected $details;
|
||||||
|
|
||||||
|
public function __construct(FlattenException $exception, $details = null)
|
||||||
|
{
|
||||||
|
$this->exception = $exception;
|
||||||
|
$this->details = $details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($method, $arguments)
|
||||||
|
{
|
||||||
|
$code = $this->exception->getStatusCode();
|
||||||
|
|
||||||
|
throw new ReceiverSoapFault(
|
||||||
|
isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
|
||||||
|
null,
|
||||||
|
$this->details
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter key="besimple.soap.response.class">BeSimple\SoapBundle\Soap\SoapResponse</parameter>
|
<parameter key="besimple.soap.response.class">BeSimple\SoapBundle\Soap\SoapResponse</parameter>
|
||||||
<parameter key="besimple.soap.response.listener.class">BeSimple\SoapBundle\EventListener\SoapResponseListener</parameter>
|
<parameter key="besimple.soap.response.listener.class">BeSimple\SoapBundle\EventListener\SoapResponseListener</parameter>
|
||||||
|
<parameter key="besimple.soap.exception_listener.class">BeSimple\SoapBundle\EventListener\SoapExceptionListener</parameter>
|
||||||
<parameter key="besimple.soap.context.class">BeSimple\SoapBundle\WebServiceContext</parameter>
|
<parameter key="besimple.soap.context.class">BeSimple\SoapBundle\WebServiceContext</parameter>
|
||||||
<parameter key="besimple.soap.binder.request_header.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestHeaderMessageBinder</parameter>
|
<parameter key="besimple.soap.binder.request_header.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestHeaderMessageBinder</parameter>
|
||||||
<parameter key="besimple.soap.binder.request.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder</parameter>
|
<parameter key="besimple.soap.binder.request.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder</parameter>
|
||||||
|
@ -25,6 +26,14 @@
|
||||||
<argument type="service" id="besimple.soap.response" />
|
<argument type="service" id="besimple.soap.response" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="besimple.soap.exception_listener" class="%besimple.soap.exception_listener.class%">
|
||||||
|
<tag name="kernel.event_subscriber" />
|
||||||
|
<tag name="monolog.logger" channel="request" />
|
||||||
|
<argument type="service" id="service_container" />
|
||||||
|
<argument>%besimple.soap.exception_listener.controller%</argument>
|
||||||
|
<argument type="service" id="logger" on-invalid="null" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="besimple.soap.context.rpcliteral" class="%besimple.soap.context.class%" abstract="true">
|
<service id="besimple.soap.context.rpcliteral" class="%besimple.soap.context.class%" abstract="true">
|
||||||
<argument type="service" id="besimple.soap.definition.loader" />
|
<argument type="service" id="besimple.soap.definition.loader" />
|
||||||
<argument type="service" id="besimple.soap.converter.collection" />
|
<argument type="service" id="besimple.soap.converter.collection" />
|
||||||
|
|
|
@ -25,10 +25,11 @@
|
||||||
"besimple/soap-common": "0.2.*",
|
"besimple/soap-common": "0.2.*",
|
||||||
"besimple/soap-wsdl": "0.2.*",
|
"besimple/soap-wsdl": "0.2.*",
|
||||||
"ass/xmlsecurity": "~1.0",
|
"ass/xmlsecurity": "~1.0",
|
||||||
|
"symfony/framework-bundle": "~2.0",
|
||||||
|
"symfony/twig-bundle": "~2.0",
|
||||||
"zendframework/zend-mail": "2.1.*",
|
"zendframework/zend-mail": "2.1.*",
|
||||||
"zendframework/zend-mime": "2.1.*",
|
"zendframework/zend-mime": "2.1.*",
|
||||||
"zendframework/zend-soap": "2.1.*",
|
"zendframework/zend-soap": "2.1.*"
|
||||||
"symfony/framework-bundle": "~2.0"
|
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"besimple/soap-client": "0.2.*",
|
"besimple/soap-client": "0.2.*",
|
||||||
|
|
Loading…
Reference in New Issue