So Im building an API.
But I keep running into returning a json response from an inner function.
If I do the following then laravel sends a backlog or error log to the client. That should never ever happen in a api. So how do you do this in Laravel?
I want to return json immediately and stop excecuting without showing the client any other information
public function functionThatIsCalledByClient($data)
{
$this->validateSomething($data);
}
private function validateSomething($data)
{
if(! $data ) ) return response()->json(['error' => 'some message'], 400)->send();
return true;
}
You could use abort helper for that or, for complex cases, error handling.
In case of abort:
private function validateSomething($data)
{
if(! $data ) // you can create a custom helper function to wrap this code.
abort(400, json_encode(['error'=>'some error']), ['Content-Type: application/json']);
return true;
}
In case of general handler:
private function validateSomething($data)
{
if(! $data )
throw new \Exception('some message');
return true;
}
Inside app/Exceptions/Handler.php # render
public function render($request, Exception $e)
{
if($e instanceof Exception)
return response()
->json(['error' => $e->getMessage()], 400);
//->send(); should not be necessary
}
throw new GeneralException('invalid required extra quantity');
Related
I'm trying to use Laravel API Resource and handle the error message by sending a specific HTTP code
Here is my code :
public function show($id)
{
try {
return FruitResource::make(Fruit::find($id));
}
catch(Exception $e)
{
throw new HttpException(500, 'My custom error message');
}
}
My try/catch is systematically ignored when I try to access the route.
I am voluntarily accessing an object that is not in the database. I have ErrorException with message Trying to get property 'id' of non-object.
I would like to be able to send my own Exception here, in case the user tries to access data that doesn't exist. And return a json error.
Try this (notice the \ before Exception):
public function show($id)
{
try {
return FruitResource::make(Fruit::find($id));
}
catch(\Exception $e)
{
throw new HttpException(500, 'My custom error message');
}
}
I am new to Laravel and have an Issue regarding the Handler.php File.
I am trying to create a class that takes an exceptions and transforms it into a JSON Response.
Sadly though, upon calling the constructor a series of Errors are thrown:
(ErrorErrorErrorErrorErrorErrorErrorErrorErrorErrorErrorSymfony\Component\ErrorHandler\Error\FatalError)
My code:
render() in Handler.php:
public function render($request, Throwable $exception)
{
$errorResource = new ErrorResource($exception);
return $errorResource->getJsonResponse();
}
class ErrorResource in ErrorResource.php:
<?php
namespace Transformers;
use Throwable;
class ErrorResource
{
private $exception;
private $defaultCodes = [TypeError::class => 400];
private $defaultMessages = [TypeError::class => 'Untgültige URL Parameter'];
function __construct(Throwable $exception)
{
$this->exception = $exception;
}
public function getJsonResponse($exception)
{
$codeToThrow = 500;
$messageToThrow = "Internal Server Error";
$type = get_class($this->exception);
if (empty($exception->getCode())) {
$codeToThrow = $this->defaultCodes[$type];
} else {
$codeToThrow = $exception->getCode();
}
if (empty($exception->getMessage())) {
$messageToThrow = $this->defaultMessages[$type];
} else {
$messageToThrow = $exception->getMessage();
}
return response()->json(array(
'Type' => $type,
'Message' => $messageToThrow
), $codeToThrow);
}
}
I have also tried to move the method getJsonResponse() to the Handler.php file and call it from there, but without any luck.
I am really confused as to why I am not allowed to do certain things with the $exception variable (I have also tried to create a clone of this object - but the same error occures)
I hope you can help me resolving this issue,
Greetins,
Franz
The issue is, that PHP is call by value. That is why it is implicitely trying to clone an unclonable object -> Error. To resolve this issue one can use wrapper objects, but I decided to simply use call by reference (https://www.javatpoint.com/php-call-by-reference)
How to test if render() an exception into an HTTP response working correctly. I want to test without calling $this->get()
This is a method in Laravel:
public function render($request, Exception $e)
{
$response['exception'] = get_class($e);
$response['message'] = $e->getMessage();
if ($e instanceof LockException) {
return $this->errorResponse('lock', $response, 'Lock error has occurred', $e->getCode());
}
return parent::render($request, $e);
}
I need to test if LockException has turn into an HTTP response.
Something like this. In your test, instantiate the controller into a variable, create a blank request and pass in your LockException:
$response = $controller->render($request, $exception);
$this->assertEquals('string of HTML you expect', $response);
I working on the Halo 5 API, and users can register with their gamer-tags. When they do, they can log in, and when they login, they can go to their profile to look up their stats for Halo 5. But obviously some users wont enter legit gamertags. And thats were I need some validation in my controller that returns the view for the profile.
Here is a simple example of the controller:
public function index ($slug) {
// Emblem URL.
$getPlayerEmblemImage = app('App\Http\Controllers\Api\GetGeneralStatsController')->getPlayerEmblemImage($slug);
// Get General Player Arena Stats
$playerGeneralStats = app('App\Http\Controllers\Api\GetGeneralStatsController')->getPlayerArenaStats($slug);
$playerGeneralStatsArray = $this->getPlayerGeneralStatsArray($playerGeneralStats);
// Get the Spartan Rank and XP
$spartanRank = json_decode($playerGeneralStatsArray['SpartanRank'], true);
$XP = json_decode($playerGeneralStatsArray['Xp'], true);
$Gamer_Tag = json_encode($playerGeneralStatsArray['Gamer_Tag'], true);
$user = User::whereSlug($slug)->firstOrFail();
return view('profile.index',
compact(
'user',
'spartanRank',
'XP',
'getPlayerEmblemImage',
'Gamer_Tag',
)
);
}
My problem is, if a user does not exist, it throws this error:
ClientException in RequestException.php line 107:
Client error: GET https://www.haloapi.com/profile/h5/profiles/some%20useer/spartan resulted in a 404 Not Found response:
How can I do some checking, and return different results, if that player is not found?
Maybe something like this?
public function index ($slug) {
if (// Doesnt exist, show this) {
// do some other code here
return view('profile.index', compact('user'))
} else {
// Code here....
return view('profile.index',
compact(
'user',
'spartanRank',
))
}
}
I think you can use exception handling here.
try{
// Code here....
return view('profile.index',
compact(
'user',
'spartanRank',
))
}
} catch(ClientException $exception) {
{
// do some other code here
return view('profile.index', compact('user'))
}
Import use GuzzleHttp\Exception\ClientException; in your controller
use GuzzleHttp\Exception\ClientException;
Got it. Change code in App/Exceptions/Handler.php
public function render($request, Exception $e)
{
// Flash a success message saying you have successfully registered.
flash()->error('Error', 'That player has not played Halo, or does not exist.');
return redirect()->back();
// return parent::render($request, $e);
}
So I am messing around with symfony router component and I created a small wrapper.
One thing that came up was how do I get a request to throw a 500 in unit tests? The method in question is:
public function processRoutes(Request $request) {
try {
$request->attributes->add($this->_matcher->match($request->getPathInfo()));
return call_user_func_array($request->attributes->get('callback'), array($request));
} catch (ResourceNotFoundException $e) {
return new RedirectResponse('/404', 302);
} catch (Exception $e) {
return new RedirectResponse('/500', 302);
}
}
And the test in question is:
public function testFiveHundred() {
$router = new Router();
$router->get('/foo/{bar}', 'foo', function($request){
return 'hello ' . $request->attributes->get('bar');
});
$response = $router->processRoutes(Request::create('/foo/bar', 'GET'));
$this->assertEquals(500, $response->getStatusCode());
}
Right now the test will fail because we are defined and the status code will be 200. Is there something special I can do to the Request object I create, to make it throw a 500?
I think you got several options here you can play with:
Decide that a specific path will always throw an exception.
This will force you to make some changes in your code.
public function processRoutes(Request $request) {
...
if ($request->getRequestUri() == '/path/that/throws/exception') {
throw Exception('Forced to throw exception by URL');
}
...
}
public function testFiveHundred() {
...
$response = $router->processRoutes(Request::create('/path/that/throws/exception', 'GET'));
...
}
Make a DummyRequest object that will extends your original Request class and make sure this object will raise an Exception (for example - you know for sure that you use the getPathInfo(), so you can use this).
class DummyRequest extends Request {
public function getPathInfo() {
throw new Exception('This dummy request object should only throw an exception so we can test our routes for problems');
}
}
public function testFiveHundred() {
...
$dummyRequest = new DummyRequest();
$response = $router->processRoutes($dummyRequest);
...
}
Since the function getRequestUri of our $dummyRequest throws an exception, your call to $router->processRoutes will have our dummy to throw that exception.
This is a general idea, you would probably need to play a bit with the namespaces and the functions there (I didn't test it, however this should work).