I have an application, which for each method it does a field validation before proceeding with code execution.
public function authenticateSeller(Request $request)
{
$fields = $request->all();
$rules = config("validation-rules.loads.access");
$validator = Validator::make($fields, $rules);
if($validator->fails()) {
throw new CustomFieldValidation($validator);
}
}
I needed to pass the object to the CustomFieldValidation class to pass to the Laravel Handler class to handle the errors and return in the JSON form. How to do this?
class CustomFieldValidation extends \Exception
{
public function __construct($validator,$message= NULL, $code = NULL, Exception $previous = NULL)
{
parent::__construct($message, $code, $previous);
}
}
I was hoping to manipulate the message handler's rendering method.
public function render($request, Exception $exception)
{
if($exception instanceof CustomFieldValidation) {
foreach($validator->errors()->all() as $message) {
$errors[] = [
'message' => $message,
];
}
return response()->json($errors, 400, ['Content-type' => 'application/json; charset=utf-8'], JSON_UNESCAPED_UNICODE);
}
return parent::render($request, $exception);
}
Can someone help me?
To answer your question: just add a $validator attribute to your exception class.
class CustomFieldValidation extends \Exception
{
public $validator;
public function __construct($validator,$message= NULL, $code = NULL, Exception $previous = NULL)
{
parent::__construct($message, $code, $previous);
$this->validator = $validator;
}
}
public function render($request, Exception $exception)
{
if($exception instanceof CustomFieldValidation) {
foreach($exception->validator->errors()->all() as $message) {
$errors[] = [
'message' => $message,
];
}
return response()->json($errors, 400, ['Content-type' => 'application/json; charset=utf-8'], JSON_UNESCAPED_UNICODE);
}
return parent::render($request, $exception);
}
#Samsquanch is right, the use case example you're showing is simple and it'd be better to just return JSON from the controller. However if you're throwing the exception from some other class, this would make sense.
Related
I can't find an explanation of why a return response() inside a catch is not stopping the execution, i guess im missing something.
In this example the request have an error so the try-catch on the service receive ValidationException. This goes to ValidationErrorResponder and here is where the execution should finish and return a json with the errors. But it continues and return the json error response through $this->updateUserResponder->respond()
I have a defined route which execute the __invoke() method on UpdateUserAction
class UpdateUserAction
{
protected $updateUserService;
protected $updateUserResponder;
public function __construct(UpdateUserService $updateUserService, UpdateUserResponder $updateUserResponder)
{
$this->updateUserService = $updateUserService;
$this->updateUserResponder = $updateUserResponder;
}
public function __invoke(Request $request, $userId)
{
$serviceData = [
'id' => $userId,
'commandPayload' => $request->only('name')
];
return $this->updateUserResponder->respond($this->updateUserService->execute($serviceData));
}
}
class UpdateUserService extends BaseService
{
public function execute(array $data = [])
{
try {
$this->bus->addHandler(UpdateUserCommand::class, UpdateUserHandler::class);
return $this->bus->dispatch(UpdateUserCommand::class, $data, [UpdateUserValidator::class]);
} catch (ValidationException $e) {
return $this->validationErrorResponder->respond($e);
}
}
}
class UpdateUserValidator implements Middleware
{
protected $rules = [
'id' => 'uuid',
'commandPayload.name' => 'max:256'
];
protected $messages = [];
public function execute($command, callable $next)
{
$validator = Validator::make((array) $command, $this->rules, $this->messages);
if ($validator->fails()) {
throw new ValidationException($validator);
}
return $next($command);
}
}
This shoudl return the final response wiht the errors in a JSON but
class ValidationErrorResponder
{
public function respond($validator)
{
$messages = $validator->getValidator()->getMessageBag()->messages();
return response()->json(['errors' => $messages], 422);
}
}
Maybe the error it's another and the catch does not working because only are catching ValidationException.
So try catching all exception to see what happens:
class UpdateUserService extends BaseService
{
public function execute(array $data = [])
{
try {
$this->bus->addHandler(UpdateUserCommand::class, UpdateUserHandler::class);
return $this->bus->dispatch(UpdateUserCommand::class, $data, [UpdateUserValidator::class]);
} catch (\Exception $e) {
return $this->validationErrorResponder->respond($e);
}
}
}
Im trying intercept errors in laravel and i found a nice way to do that:
Simulating a error:
public function index(){
$users = User::all(); //<-SQL exeception here
return response()->json(['message'=>'ok'], 200);
}
app/Exceptions/Handler.php
public function report(Exception $exception)
{
dd($exception); //<-intercept my error here
parent::report($exception);
}
Works very well and i can do whatever i want with error, but when i use a try-catch block, my interceptor does not work:
Simulating error again
public function index(){
try {
$users = User::all();//<-SQL exeception here
} catch (\Throwable $th) {
error_log('Error handled');
//MyInterceptor::manuallyIntercept($th);
}
return response()->json(['message'=>'ok'], 200);
}
Is there a clean way to intercept all handled errors programatically?
Not report method, you need to use render method on Handler.php
You will see $this->errorResponse which is to just return JSON response. I just want to show the main idea.
public function render($request, Exception $exception)
{
if ($exception instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($exception, $request);
}
if ($exception instanceof ModelNotFoundException) {
$modelName = strtolower(class_basename($exception->getModel()));
return $this->errorResponse("Does not exists any {$modelName} with the specified identificator", 404);
}
if ($exception instanceof AuthenticationException) {
return $this->unauthenticated($request, $exception);
}
if ($exception instanceof AuthorizationException) {
return $this->errorResponse($exception->getMessage(), 403);
}
if ($exception instanceof MethodNotAllowedHttpException) {
return $this->errorResponse('The specified method for the request is invalid', 405);
}
if ($exception instanceof NotFoundHttpException) {
return $this->errorResponse('The specified URL cannot be found', 404);
}
if ($exception instanceof HttpException) {
return $this->errorResponse($exception->getMessage(), $exception->getStatusCode());
}
if ($exception instanceof QueryException) {
$errorCode = $exception->errorInfo[1];
if ($errorCode == 1451) {
return $this->errorResponse('Cannot remove this resource permanently. It is related with any other resource', 409);
}
}
if (config('app.debug')) {
return parent::render($request, $exception);
}
return $this->errorResponse('Unexpected Exception. Try later', 500);
}
Error response method
protected function errorResponse($message, $code)
{
return response()->json(['error' => $message, 'code' => $code], $code);
}
I want to send my own parameters into custom exception using throw new CustomException.
I've defined my own error codes into Config and these are necessary for me.
public function saveMenu($data){
$validatedData = Validator::make($data, array(
'id' => 'required',
'menuID' => 'required',
));
try {
$validatedData->validate();
} catch \Exception $e){
$data= array(
'status' => false,
'code' => 599
'message' => config('exception_codes.599').'<br/>'.$validatedData->errors(),
'data' => []
);
throw new CustomException($data ['message'], $data['code'],$e);
return $data;
}
}
class Handler extends ExceptionHandler
{
protected $dontReport = [
//
];
protected $dontFlash = [
'password',
'password_confirmation',
];
public function report( Exception $exception=NULL)
{
parent::report($exception);
}
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
}
class CustomException extends Exception{
public $code;
public $message;
public $trace;
public function __construct( $message, $code, Exception $exception=NULL)
{
parent::__construct($message, $code, $exception);
$this->code = $code;
$this->message=$message;
$this->trace= $exception->getTrace();
}
public function report($message, $code, Exception $exception=NULL)
{
$message = $exception->getMessage();
$xx= new Menu();
$xx->setFile($this->trace[1]['file']);
$xx->setLine($this->trace[1]['line']);
$xx->setFunction($this->trace[1]['function']);
$xx->setClass($this->trace[1]['class']);
$xx->setMessage($message);
$xx->save();
parent::report($exception);
}
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
I get this error;
Too few arguments to function
App\Exceptions\CustomException::report(), 0 passed in
C:......\Handler.php on line 102 and at least 2 expected"
Can you help me?
There's no need to pass in the message and code explicitly in the report() method.
public function report()
{
$message = $this->getMessage();
$hataTakip = new HataTakip();
$hataTakip->setFile($this->trace[1]['file']);
$hataTakip->setLine($this->trace[1]['line']);
$hataTakip->setFunction($this->trace[1]['function']);
$hataTakip->setClass($this->trace[1]['class']);
$hataTakip->setMessage($message);
$hataTakip->save();
parent::report($this);
}
In any case, you should check out the documentation en Error Handling.
We have created an API that connects to the SOAP API system of the customer. We have a custom exception CustomerNotFound :
<?php
namespace App\Exceptions;
class CustomerNotFoundException extends \Exception
{
protected $code = 'customer_not_found';
protected $message = 'Customer not found!';
}
A Soap Class function for retrieving user information
public function getCustomerById($customerID)
{
$parameters = $this->appendToParameters([
'customerID' => $customerID])
->getParameters();
$result = $this->client->GetCustomerByID($parameters);
if ($result->SDKResult->ResultCode === 'Success') {
$data = $result->GetCustomerByIDResult;
return $data;
}
throw new CustomerNotFoundException();
}
And a controller that calls the soap class function:
public function getCustomerDetails(Request $request, $customerId)
{
try {
$customerDetails = $this->customerService->getCustomerById($customerId);
return $this->respond($customerDetails);
} catch (\SoapFault $e) {
$this->respondInternalError($e->getMessage());
} catch (\App\Exceptions\CustomerNotFoundException $c) {
$this->respondNotFound($c->getMessage());
}
}
I know the exception is working because when the soap function is also called on another controller, it works and then catches the exception properly when an invalid customer id is given. Also when I remove the try catch block and call the controller function with and invalid id, the api returns the CustomerNotFound exception. However, when it is inside a try catch block it just returns a STATUS 200 and an empty response (no jJSON, etc.). No errors are shown. Any ideas?
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Response;
use JWTAuth;
class ApiController extends Controller
{
protected $statusCode = 200;
public function isDevMode(){
return request()->get('__devmode__', null) == 'ph';
}
public function err($msg, $code = 'error'){
return $this->respond([
'data'=> null,
'status'=> [
'code'=> $code,
'msg'=> $msg,
'success'=> false
]
]);
}
public function ok($data =[],$msg='Success',$code='success'){
return $this->respond([
'data'=> $data,
'status'=> [
'code'=> $code,
'msg'=> $msg,
'success'=> true
]
]);
}
public function getStatusCode()
{
return $this->statusCode;
}
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
public function respondNotFound($message = 'Not Found.',$code='not_found')
{
return $this->setStatusCode(404)->respondWithError($message,$code);
}
public function respondInternalError($message = 'Internal Server Error.', $code='internal_server_error')
{
return $this->setStatusCode(500)->respondWithError($message,$code);
}
public function respond($data, $headers = ['Content-type'=> 'application/json; charset=utf-8'])
{
return Response::json($data, $this->getStatusCode(), $headers);
}
public function respondWithError($message, $code = 'error')
{
return $this->respond([
'error' => [
'code' => $code,
'message' => $message
// 'status_code' => $this->getStatusCode()
]
]);
}
public function getUserTokenDetails()
{
$token = JWTAuth::getToken()->get();
try {
if (! $user = JWTAuth::parseToken())
{
return response()->json(['user_not_found'], 404);
}
}
catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e)
{
$this->setStatusCode(401);
return $this->respondWithError('Token has expired.','token_expired');
}
catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e)
{
$this->setStatusCode(401);
return $this->respondWithError('Token is invalid','token_invalid');
}
catch (Tymon\JWTAuth\Exceptions\JWTException $e)
{
$this->setStatusCode(401);
return $this->respondWithError('Token not found.','token_not_found');
}
$payload = JWTAuth::parseToken()->getPayload();
$user = $payload['customClaims'];
return $user['CUST_ID'];
}
}
I've got a problem with error messages during validation in Laravel. I've got my custom Request:
class CreateCv extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required',
'surname' => 'required',
];
}
public function messages()
{
return [
'name.required' => 'Fill the name!',
'surname.required' => 'Fill the surname!',
];
}
}
and then I've got a controller with create method:
class CvController extends Controller
{
public function create(Requests\CreateCv $request) {
return response()->json(['ok' => true], 200);
}
}
If everything is filled up, then it returns json response "ok" : true correctly. But when something is missing, then it returns empty msg: "msg": "". What causes this problem?
EDIT
here's the handler.php file
class Handler extends ExceptionHandler
{
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
//ValidationException::class,
];
public function report(Exception $e)
{
parent::report($e);
}
public function render($request, Exception $e)
{
return $e;
//return response()->json(['msg' => $e->getMessage()], 422);
//return parent::render($request, $e);
}
}
Can you try in your CreateCv class to extend Illuminate\Foundation\Http\FormRequest.
I encountered this recently during creating an API for a web application, every time I test the validation fields without the Accept application/json header, the errors object (I customized the ValidationException to return an errors object) is always empty.