Working on a new repo. After doing some research, I've decided to use API Resources to standardize my API responses, according to jsonapi.org best practices.
I am not able to get a good answer on the best way to return consistent user-readable error messages (not exceptions). These are messages that can potentially be returned directly from the controller. Using Laravel's API Resources, I've been able to create something like this, but it feels hacky.
$error = (object) (['errorCode' => 422, "messageDetail" => ["First name must contain at least three characters."]]);
return new ErrorResource($error);
ErrorResource is used to format the JSON in this case. The thinking is that whenever a developer wants to code up an error message, that they would use ErrorResource.
Is there is a better way?
inside App/Exceptions/Handler.php you can change render function as per your requirement and return the Resource from this function. Here is an example
public function render($request, Exception $exception)
{
if ($request->is('api/*') || $request->expectsJson() || $request->is('webhook/*')) {
$error = (object) (['errorCode' => 422, "messageDetail" => ["First name must contain at least three characters."]]);
return new ErrorResource($error);
}
return parent::render($request, $exception);
}
For me better do error formatting in app/Exceptions/Handler.php:render.
This is a more flexible approach and helps to do it in one place.
Related
I'm currently implementing a Services Oriented Architecture in Symfony2, and I would like to have our recommandations about it.
I want to know what I need to handle in the Controller, and what I need to handle in the service.
An extreme solution would be to pass the Request to the service, and it will implement all the logic (mostly Doctrine, as I'm developping an API and not a "full" site with Twig for example), and then send back a Responsein the controller.
That means I would have to create code like this :
In the service :
if (null === $entity) {
throw new \Exception('Not found.', self::NOT_FOUND);
}
And in the controller :
try {
$service->doThings();
}
catch (\Exception $e) {
if ($e->getCode() === Service::NOT_FOUND) {
return new Response($e->getMessage(), 404);
}
}
With potentially one condition for every exception I need to throw.
Is this a good way to go ? Am I completly wrong about the implementation ?
Everything that is related to controllers logic (such as, take a request, response to a request, make a form, bind parameters, extract an entity from db (better with ParamConverter), get and set session objects, redirects, and so on) should be kept into controllers.
Everything else could be migrated into services (or other classes)
I'm new to Laravel (we're using 5.0 at work). Right now, when we respond to an API request in a Controller, we are rewriting the same code over and over to respond to unauthorized actions. For example,
public function getUsers(){
if (Entrust::can('users.view')){
$users = Users::get();
return response()->done($users, 200);
} else {
return response()->unauthorized('users.view');
}
}
It gets more and more complicated if we have different permissions that can allow an API request to succeed.
I'd like to simply throw an exception of some sort if the user cannot perform the API request. For example,
public function getUsers(){
require('users.view'); // throws an UnauthorizedException if current user doesn't have 'users.view' permission
$users = User::get();
return response()->done($users, 200);
}
public function someOtherMethod(){
if (!Entrust::can('permission1') && !Entrust::can('permission2')){
throw new UnauthorizedException(['permission1', 'permission2']);
}
// some other stuff
}
But I don't know what code calls the API function, nor where to wrap that call in a try/catch. It's easy enough to code the UnauthorizedException, and easy to transform it into json, but where do I put the handler? As I said, I'm new to Laravel, and I don't know how it handles these exceptions.
Ideally, whatever solution I find, I'd like to extend it to other exceptions so we can have consistent json responses based on common exceptions.
Instead of repeating your code, take a look at implementing the authorization check with Middleware.
I am returning response from onBootStrap() this way..
$app = $e->getApplication();
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($app) {
$response = $e->getResponse();
$response->setStatusCode(403);
$jsonValue = json_encode(array(
'error' => 12345,
'message' => 'You are not authorized for this request',
));
$response->setContent($jsonValue);
return $response;
}, PHP_INT_MAX);
But the problem is I am getting status code 200 even if I am passing different ones.
I am running this API from Advanced rest API client.
Before:
You need to interrupt the normal course of processing a request. See module BjyAuthorize. It generates an error: https://github.com/bjyoungblood/BjyAuthorize/blob/master/src/BjyAuthorize/Guard/Route.php#L69 Here it is processed: https://github.com/bjyoungblood/BjyAuthorize/blob/master/src/BjyAuthorize/View/UnauthorizedStrategy.php#L53
In the onBootstrap method you can add a listener for the event MvcEvent::EVENT_DISPATCH_ERROR. It will check whether the error is an Auth error and the need to set the status code of the response and its contents. Auth code will trigger an event MvcEvent::EVENT_DISPATCH_ERROR and set "event error" $event->setError(static::ERROR)
After:
This is not the best question is not the best answer. The best answer would be "use standard modules". Nevertheless , there is such a thing as "the ultimate complexity of the system". A sign that the system has reached the limit of complexity is that users find it easier to write your own code and not use the standard. However, there is an objective and a subjective complexity. Objective - standard modules are not very complex. Nevertheless, they are not documented in the best way . Therefore, I believe that my answer aims to reduce the complexity for you a subjective standard system , in this case - the module BjyAuthorize. You can specify your own strategy as follows: 'unauthorized_strategy' => 'MyModule\Listener\UnauthorizedStrategy'
Many web application projects I've been involved with reach a point where
The application expects persisted data to be in a particular format
The application will barf if the persisted data strays from that format
Old "mystery code" is persisting data in the bad format
This usually results in the application developers cluttering the model code with lots of validation conditionals. That is
function save()
{
if($model->getSomeProp() == 'bad value')
{
$model->setSomeProp('good default value');
}
return parent::save();
}
Are there better patterns and/or systems for dealing with these situations, with said patterns and/or systems not relying on having the developers writing perfect migration scripts and/or validation code for every release? My specific interest is in how other developers approach cleaning up these sorts of (in my experience) common long-term problems.
Specifically looking for a LAMP Stack/PHP solution, but solutions and approaches from other common middleware languages/platforms (ruby, python, etc.) are more than welcome.
We use configuration-file provided list of steps that should be performed on every processed item. That enables validation, slight changes of data, lookups, retrieval and merging of certain attributes from external sources etc.
Although right now it is based on a set of Ruby classes that implement abstract Step, working according to yaml configuration, I guess that in the next rewrite I would go with pure Ruby DSL.
So at the end, you would have something like this:
HealingProcessor.on(impure_data) {
replace_bad_value :field => :some_prop, :bad_value => 'bad value', :good_value => 'good_default_value'
# etc
}
At least you are handling this type of behavior as close to the db interaction as possible, it could be a lot worse if your code-base were littered with these types of checks.
If I were tasked with cleaning this type of thing up, I think the first thing I would do is set the method to throw a custom exception code, that way I can log the calling code and find which part of my application is formatting data in an incorrect fashion.
For example you could do something like:
class CustomException extends Exception
{
const CODE_BAD_FORMAT = 1;
protected code;
public function setCode($code)
{
$this->code = $code;
}
public function getCode()
{
return $this->code;
}
}
class Model extends ParentModel
{
function save()
{
if ($model->getSomeProp() == 'bad value') {
$badValueFound = true;
$model->setSomeProp('good default value');
}
// Now that you are using try/catches you don't need a return value
parent::save();
if ($badValueFound) {
$e = new CustomException();
$e->setCode(CustomException::CODE_BAD_FORMAT);
throw $e;
}
}
}
// Calling code
try {
$model = new Model();
$model->setSomeProp('ohnoes im bad format');
$model->save();
} catch (Exception $e) {
if ($e->getCode() === CustomException::CODE_BAD_FORMAT) {
error_log(__METHOD__ . ': Called save with bad format');
} else {
throw $e; // Some other exception occurred b/c the code() didn't line up so bubble up
}
}
// All is well b/c made it through the try / catch block... so onto the next logic
Now, you can make the call to save(), and if a bad format is encountered, you can throw an exception and check the code from the call, if the code matches (expected bad format) then you can implement some logging track calling code-points.
Plus, you don't break anything in the process, because the save is still going to happen, however you will have to ensure any calls to save() are wrapped in a try/catch block, otherwise you will get exceptions if not caught properly.
Another idea might be track the bad format constructs in the model classes so you don't end up copying the same strings all over the place:
class Model
{
const BAD_FORMAT_MALFORMED_NAME = 'format for a malformed name';
const BAD_FORMAT_MALFORMED_ADDRESS = 'format for malformed address';
}
....
if($model->getSomeProp() === self::BAD_FORMAT_MALFORMED_NAME) {
....
How should I write error reporting modules in PHP?
Say, I want to write a function in PHP: 'bool isDuplicateEmail($email)'.
In that function, I want to check if the $email is already present in the database.
It will return 'true', if exists. Else 'false'.
Now, the query execution can also fail, In that time I want to report 'Internal Error' to the user.
The function should not die with typical mysql error: die(mysql_error(). My web app has two interfaces: browser and email(You can perform certain actions by sending an email).
In both cases it should report error in good aesthetic.
Do I really have to use exception handling for this?
Can anyone point me to some good PHP project where I can learn how to design robust PHP web-app?
In my PHP projects, I have tried several different tacts. I've come to the following solution which seems to work well for me:
First, any major PHP application I write has some sort of central singleton that manages application-level data and behaviors. The "Application" object. I mention that here because I use this object to collect generated feedback from every other module. The rendering module can query the application object for the feedback it deems should be displayed to the user.
On a lower-level, every class is derived from some base class that contains error management methods. For example an "AddError(code,string,global)" and "GetErrors()" and "ClearErrors". The "AddError" method does two things: stores a local copy of that error in an instance-specific array for that object and (optionally) notifies the application object of this error ("global" is a boolean) which then stores that error for future use in rendering.
So now here's how it works in practice:
Note that 'Object' defines the following methods: AddError ClearErrors GetErrorCodes GetErrorsAsStrings GetErrorCount and maybe HasError for convenience
// $GLOBALS['app'] = new Application();
class MyObject extends Object
{
/**
* #return bool Returns false if failed
*/
public function DoThing()
{
$this->ClearErrors();
if ([something succeeded])
{
return true;
}
else
{
$this->AddError(ERR_OP_FAILED,"Thing could not be done");
return false;
}
}
}
$ob = new MyObject();
if ($ob->DoThing())
{
echo 'Success.';
}
else
{
// Right now, i may not really care *why* it didn't work (the user
// may want to know about the problem, though (see below).
$ob->TrySomethingElse();
}
// ...LATER ON IN THE RENDERING MODULE
echo implode('<br/>',$GLOBALS['app']->GetErrorsAsStrings());
The reason I like this is because:
I hate exceptions because I personally believe they make code more convoluted that it needs to be
Sometimes you just need to know that a function succeeded or failed and not exactly what went wrong
A lot of times you don't need a specific error code but you need a specific error string and you don't want to create an error code for every single possible error condition. Sometimes you really just want to use an "opfailed" code but go into some detail for the user's sake in the string itself. This allows for that flexibility
Having two error collection locations (the local level for use by the calling algorithm and global level for use by rendering modules for telling the user about them) has really worked for me to give each functional area exactly what it needs to get things done.
Using MVC, i always use some sort of default error/exception handler, where actions with exceptions (and no own error-/exceptionhandling) will be caught.
There you could decide to answer via email or browser-response, and it will always have the same look :)
I'd use a framework like Zend Framework that has a thorough exception handling mechanism built all through it.
Look into exception handling and error handling in the php manual. Also read the comments at the bottom, very useful.
There's aslo a method explained in those page how to convert PHP errors into exceptions, so you only deal with exceptions (for the most part).