I want my controller to return a 404 response when a model is not found and I want to specify a custom message, not the default "The requested controller was unable to dispatch the request."
I have tried specifying the reason in the ViewModel, setting the reasonPhrase from the response object... nothing seems to work. I am currently investigating how I can prevent the default behaviour, but if someone know before I do, that would just be great. (Perhaps there's a better way than the one I would find anyhow, too.)
Here is what I have, which does not work :
$userModel = $this->getUserModel();
if (empty($userModel)) {
$this->response->setStatusCode(404);
$this->response->setReasonPhrase('error-user-not-found');
return new ViewModel(array(
'content' => 'User not found',
));
}
Thanks.
Looks like you are confusing the reasponphrase and the reason variable passed on to the view. The reasonphrase is part of the http status code, like "Not Found" for 404. You probably don't want to change that.
Like #dphn is saying, I would recommend throwing your own Exception and attach a listener to the MvcEvent::EVENT_DISPATCH_ERROR which decides what to respond.
To get you started:
Controller
public function someAction()
{
throw new \Application\Exception\MyUserNotFoundException('This user does not exist');
}
Module
public function onBootstrap(MvcEvent $e)
{
$events = $e->getApplication()->getEventManager();
$events->attach(
MvcEvent::EVENT_DISPATCH_ERROR,
function(MvcEvent $e) {
$exception = $e->getParam('exception');
if (! $exception instanceof \Application\Exception\MyUserNotFoundException) {
return;
}
$model = new ViewModel(array(
'message' => $exception->getMessage(),
'reason' => 'error-user-not-found',
'exception' => $exception,
));
$model->setTemplate('error/application_error');
$e->getViewModel()->addChild($model);
$response = $e->getResponse();
$response->setStatusCode(404);
$e->stopPropagation();
return $model;
},
100
);
}
error/application_error.phtml
<h1><?php echo 'A ' . $this->exception->getStatusCode() . ' error occurred ?></h1>
<h2><?php echo $this->message ?></h2>
<?php
switch ($this->reason) {
case 'error-user-not-found':
$reasonMessage = 'User not found';
break;
}
echo $reasonMessage;
module.config.php
'view_manager' => array(
'error/application_error' => __DIR__ . '/../view/error/application_error.phtml',
),
Related
I'm trying to modify my App\Exceptions\Handler to pass the request (and therefore current URL) through to all exceptions. For this reason I need the lowest-level exception class I can get hold of to type-hint to the ->renderable() method.
Laravel/Symfony's HttpException works but only for HTTP errors, leaving out all non-HTTP exceptions. PHP's Exception class works when using getCode() instead of getStatusCode(), but always returns a "0" for both HTTP errors and exceptions. Is there another low-level exception class that will work for my purposes, or otherwise any other way to accomplish what I'm trying to do here?
public function register()
{
$this->renderable(function (Exception $exception, $request) {
$url = $request->fullUrl();
$status = $exception->getCode();
Log::warning("Error $status when trying to visit $url. Received the following message: " . $exception->getMessage());
return response()->view("errors.$status", [
"exception" => $exception
],
$status
);
});
}
}
For what it's worth, I'm using the following web routes to trigger exceptions and HTTP errors for testing:
if (app()->environment('local')) {
Route::get("/exception", function (){
throw new JsonException; // chosen because it's one of the few Laravel exceptions
// that doesn't seem to automatically resolve to a HTTP error
});
}
if (app()->environment('local')) {
Route::get("/fail/{status}", function ($status){
abort($status);
});
}
As requested, this is what I have in my Handler. I use some custom logging, and I want to make sure I grab the right code when it's an HTTP error.
public function report(Throwable $e)
{
$code = match (get_class($e)) {
'Symfony\Component\HttpKernel\Exception\NotFoundHttpException' => 404,
\HttpException::class => $e->getStatusCode(),
default => 'No Code',
};
// more stuff here
}
You can use $e->getCode() for your default as well
You can throw your JsonException and abort like so with a given code and the handler should grab it from getCode like so
// in your controller
throw new \JsonException('Something went wrong', 500);
// or
abort(500, 'Something went wrong')
// in your handler
$status = $e->getCode(); // 500
$message = $e->getMessage(); // "Something went wrong"
That said it's better to keep them as semantically separate as possible in my opinion, and let the handler do the handling depending on what it receives.
I finally managed to figure this out in the end. It's probably not the cleanest solution, but it works perfectly for my needs.
It works by inspecting each instance of the Exception class and using PHP's instanceof() to check whether it's a HTTP exception or not. If it is, it gets logged with the request URL and returns a view with a status code. If it's a generic non-HTTP exception, it gets logged with the request URL and returns another view with no status code (or you can keep the default exception behaviour by removing the return block, which renders a blank screen in production).
public function register()
{
$this->renderable(function (Exception $exception, $request) {
$url = $request->fullUrl();
if ($exception instanceof HttpException) {
$status = $exception->getStatusCode();
Log::warning("Error $status occurred when trying to visit $url. Received the following message: " . $exception->getMessage());
return response()->view("errors.error", [
"exception" => $exception,
"status" => $status
],
$status
);
} else {
$status = $exception->getCode();
Log::warning("Exception $status occurred when trying to visit $url. Received the following message: " . $exception->getMessage());
return response()->view("errors.exception", [
"exception" => $exception,
"status" => $status
]);
}
});
// Optionally suppress all Laravel's default logging for exceptions, so only your own logs go to the logfile
$this->reportable(function (Exception $e) {
})->stop();
}
I'm using laravel to produce data to confluent cloud kafka, but recenly I got some error message
Curl error (code 6): getaddrinfo() thread failed to start, what is actualy causing this error ?
the ulimit is already increased
here is my code to produce data to kafka
namespace App\Services;
use App\Helpers\LogActivity;
use Illuminate\Support\Facades\Log;
class KafkaProduceServices{
public function produce($topic, $data, $flushTime = 60000){
Log::info("CALL KafkaProduceServices FOR TOPIC ".$topic);
try {
$conf = new \RdKafka\Conf();
$conf->set('bootstrap.servers', env('KAFKA_BOOTSTRAP_SERVER'));
$conf->set('security.protocol', env('KAFKA_SASL_PROTOCOL'));
$conf->set('sasl.mechanism', env('KAFKA_SASL_MECHANISM'));
$conf->set('sasl.username', env('KAFKA_SASL_API_KEY'));
$conf->set('sasl.password', env('KAFKA_SASL_API_SECRET'));
$producer = new \RdKafka\Producer($conf);
$topic_produce = $producer->newTopic($topic);
$topic_produce->produce(RD_KAFKA_PARTITION_UA, 0, $data);
$result = $producer->flush($flushTime);
if (RD_KAFKA_RESP_ERR_NO_ERROR !== $result) {
Log::info("END CALL KafkaProduceServices(ERROR) FOR TOPIC ".$topic);
return [
'error' => true,
'message' => 'Was unable to flush, messages might be lost!, error: '. json_encode($result),
];
}
Log::info("END CALL KafkaProduceServices(SUCCESS) FOR TOPIC ".$topic);
return [
'error' => false,
'message' => 'Message was sended'
];
} catch (\Throwable $th) {
Log::info("END CALL KafkaProduceServices(ERROR CATCH) FOR TOPIC ".$topic);
LogActivity::makeErrorLog('CORE','KAFKA PRODUCE',$topic,$th->getMessage(),json_encode($data)); // sent error to slack
return [
'error' => true,
'message' => $th->getMessage()
];
}
}
}
I'm using this function in queue laravel
I am using symfony2.8 and
We have a KernelExceptionService and I want to skip it if there is any Exception like 500,400 or any and get back to service and continue the work.
The reason
We are hitting multiple url to fetch the data and if there is any exception occurred whole processing get stopped.
public function onKernelException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
$response = new JsonResponse;
$request = $event->getRequest();
if ($exception instanceof InvalidConfigurationException) {
//500 case
$responseData = return [
'code' => Response::HTTP_NOT_FOUND,
'message' => $exception->getMessage()
];
} else {
// same as aobve if with difference code
}
//Prepare the response
$response->setData($responseData);
$response->setStatusCode($statusCode);
$event->setResponse($response);
}
Just wrap the particular code with a try catch block?
That way your exception listener will never trigger and you can handle the exception differently in that specific part of code.
function order_confirmationAction($order,$token) {
$client = new \GuzzleHttp\Client();
$answer = $client->post("http://www.fullcommerce.com/rest/public/Qtyresponse",
array('body' => $order)
);
$answer = json_decode($answer);
if ($answer->status=="ACK") {
return $this->render('AcmeDapiBundle:Orders:ack.html.twig', array(
'message' => $answer->message,
));
} else throw new \Symfony\Component\HttpKernel\Exception\HttpException(500, $answer->message);
}
If $client->post() response status code is an "Error 500" Symfony stops the script execution and throw new exception before the json decoding.
How can I force Symfony to ignore $client->post() bad response and execute till the last if statement?
$client = new \GuzzleHttp\Client();
try {
$answer = $client->post("http://www.fullcommerce.com/rest/public/Qtyresponse",
array('body' => $serialized_order)
);
}
catch (\GuzzleHttp\Exception\ServerException $e) {
if ($e->hasResponse()) {
$m = $e->getResponse()->json();
throw new \Symfony\Component\HttpKernel\Exception\HttpException(500, $m['result']['message']);
}
}
I solved like this. In that way I can access to responses of remote server even if it returns an error 500 code.
Per Guzzle documentation:
Guzzle throws exceptions for errors that occur during a transfer.
Specifically, if the API responds with a 500 HTTP error, you shouldn't expect its content to be JSON, and you don't want to parse it, so you're better off re-throwing an exception from there already (or informing the user that something went wrong). I would suggest trying this out:
function order_confirmationAction($order, $token) {
$client = new \GuzzleHttp\Client();
try {
$answer = $client->post("http://www.fullcommerce.com/rest/public/Qtyresponse",
array('body' => $order)
);
}
catch (Exception $e) {
throw new \Symfony\Component\HttpKernel\Exception\HttpException(500, $e->getMessage());
}
$answer = json_decode($answer);
if ($answer->status=="ACK") {
return $this->render('AcmeDapiBundle:Orders:ack.html.twig', array(
'message' => $answer->message,
));
} else {
throw new \Symfony\Component\HttpKernel\Exception\HttpException(500, $answer->message);
}
}
It is probably also a good idea to check for errors when JSON-decoding the response, because there could be surprises in the content you're getting (eg. wrong format, missing or unexpected fields or values, etc.).
My goal is get a JSON like
{
"meta": {
"error_type": "error type",
"code": 400,
"error_message": "error msg"
}
}
In case something went wrong.
I tried to put the try catch block both in the rest controller's action and in the model but I get the whole exception stack (I mean with the layout + view)
What's the right way ?
Catch the exception in the controller action.
Return a JsonModel from the action containing exception information:
public function someAction()
{
try {
throw new Exception();
}
catch (Exception $e) {
return new JsonModel(array(
'meta' => array(
'code' => $e->getCode(),
'error_message' => $e->getMessage(),
//...
)
));
}
//...
}
Source: Returning JSON from a ZF2 controller action
[I tried to put the try catch block both in the action rest
controller]
I've just tried like
(I want my goal become true but
when only if something goes wrong :) )
public function create($data)
{
try{
$artist = $this->getRequest()->getPost('artist', null);
$title = $this->getRequest()->getPost('title', null);
$album = new Album();
$album->exchangeArray(array('artist'=>$artist,'title'=>$title));
$id = $this->getAlbumTable()->saveAlbum($album);
return $this->get($id);
}
catch (Exception $e) {
return new JsonModel(array(
'meta' =>array(
'code'=>500,
'error-num'=>$e->getCode(),
'error-msg'=>$e->getMessage(),
)
));
}
}
but as above it doesn't work
instead of json data i get the
whole default exception stack with layout.