概要
- Slim 3に、カスタムのException(BadRequestException)を追加する
- BadRequestExceptionがthrowされた場合、HTTP Status Code 400とエラーメッセージを返す
- Controller等でValidationがNGの場合、
throw BadRequestException('リクエストが不正です');
だけすればOKに
前置き
- 追加する前にSlimがどのようにExceptionをハンドリングしているか見てみる
どこでExceptionがcatchされるか
- Controllerなどの内部で
try cache
がなければ、Slim\Appクラスのprocessメソッドでcachtされる - handleExceptionメソッドが呼ばれる
vender/slim/Slim/App.php
public function process(ServerRequestInterface $request, ResponseInterface $response)
{
// ~省略~
// Traverse middleware stack
try {
$response = $this->callMiddlewareStack($request, $response);
} catch (Exception $e) {
$response = $this->handleException($e, $request, $response); // ←ココ
} catch (Throwable $e) {
$response = $this->handlePhpError($e, $request, $response);
}
$response = $this->finalize($response);
return $response;
}
- handleExceptionメソッドは、Exceptionのinstance毎に対応するコンテナに登録されているHandlerを呼ぶ
- NotAllowed、NotFound、SlimException以外のExceptionがthrowされた場合、
errorHandler
としてコンテナに登録されているHandlerが呼ばれる - コンテナの
errorHandler
には、デフォルトでSlim\Handlers\Errorクラスが登録されている
vender/slim/Slim/App.php
protected function handleException(Exception $e, ServerRequestInterface $request, ResponseInterface $response)
{
if ($e instanceof MethodNotAllowedException) {
$handler = 'notAllowedHandler';
$params = [$e->getRequest(), $e->getResponse(), $e->getAllowedMethods()];
} elseif ($e instanceof NotFoundException) {
$handler = 'notFoundHandler';
$params = [$e->getRequest(), $e->getResponse()];
} elseif ($e instanceof SlimException) {
// This is a Stop exception and contains the response
return $e->getResponse();
} else {
// Other exception, use $request and $response params
$handler = 'errorHandler';
$params = [$request, $response, $e];
}
if ($this->container->has($handler)) {
$callable = $this->container->get($handler);
// Call the registered handler
return call_user_func_array($callable, $params); // ←ここでコンテナのhandlerが呼ばれる
}
// No handlers found, so just throw the exception
throw $e;
}
- なので、BadRequestExceptionをハンドリングするHandlerを作成し、コンテナの
errorHandler
に上書き登録しておけばよい
実装
前置きが長くなってしまったが、本題に入る
BadRequestExceptionクラスとerrorHandler
に登録するHandlerを作成する
BadRequestExceptionの作成
- Exceptionを継承するだけ。今回は特に特別な処理は行わない
app/exception/BadRequestException.php
<?php
namespace App\Exception;
use Exception;
class BadRequestException extends Exception
{
}
Handlerの作成
- Slim\Handlers\Errorクラスを継承することで、BadRequest以外の処理は Slim\Handlers\Errorで処理されるようにする
app/handler/error.php
<?php
namespace App\Handler;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use App\Exception\BadRequestException;
final class Error extends \Slim\Handlers\Error
{
public function __construct($displayErrorDetails)
{
// エラーの詳細表示の有無設定(Slim\Handlers\Errorで必須)
parent::__construct($displayErrorDetails);
}
public function __invoke(Request $request, Response $response, \Exception $e)
{
if ($e instanceof BadRequestException)
{
// Jsonで返す
$response = $response
->withStatus(400)
->withHeader('Content-type', 'application/json')
->withJson(['message' => $e->getMessage()]);
} else {
// BadRequest以外のExceptionはSlim\Handlers\Errorへ
$response = parent::__invoke($request, $response, $e);
}
return $response;
}
}
コンテナにHandlerを登録
- dependencies.phpにて、
errorHandler
にErrorクラスを上書き登録
dependencies.php
// ↓を追加
$container['errorHandler'] = function ($c) {
return new App\Handler\Error($c->get('settings')['displayErrorDetails']);
};
以上で、throw BadRequestException($error_message);
をすれば、HTTP Status Code 400のResponseが返るはず。