-
-
Notifications
You must be signed in to change notification settings - Fork 316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consistent JSON API incl. Proper Handling of Errors (2nd half of big refactoring) #1217
Conversation
Added requirement for admin right to User::list, dropped unneccessary `login` middleware for get/set email. (AccessControll::user throws an unauthenticated exception anyway.)
…rs happens again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a review on my own so that GIThub memorizes the files which I have viewed and checked.
This appears to be fixed now. |
filter_var($value, FILTER_VALIDATE_INT) !== false && | ||
intval($value) > 0 | ||
) || ( | ||
is_int($value) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Legacy ID translation fails because of this condition (is_int
). I can make it work by changing it to is_numeric
but then I'm not sure what would be the intended difference between this case and the one right above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed this in LycheeOrg/Lychee-front@cd6df80 and in 6c99d6a.
The difference between is_int
and is_numeric
is that the latter also excepts numbers encoded as strings. But I really wanted is_int
now after we have proper JSON everywhere. But I missed to update the rest.
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
Summary
Custom Exceptions with Optional HTTP Status Code
A lot of custom exception classes have been created. They are organized as follows:
All exceptions realize the interface
LycheeException
. This interface does not declare any methods. It is only used for grouping all Lychee exceptions. Lychee exceptions are categorized into external and internal exceptions via the interfacesExternalLycheeException
andInternalLycheeException
.External exceptions are meant to be returned to the frontend. To foster this, external Lychee exceptions must also realize
HttpExceptionInterface
to provide meaningful HTTP status codes to the frontend. In particular, the message text of external Lychee exceptions shouldInternal exceptions are used for internal error propagation. While internal exceptions bubble up the call graph they should become wrapped into an external exception at some point.
Ideally, the methods of the HTTP controllers eventually only throw external exceptions. However, if an internal exception slips through the exception handler will take care of that and return a generic
500 Internal Server Error
to the frontend as a last line of defense to avoid unintended leakage of sensitive information.Revised Exception Handler
The exception handler
App\Exceptions\Handler
has heavily been revised.Correct content type
The old method
render
is gone. This method was bad, because it did not honor the content type which was expected by the client but unconditionally returned a HTML representation of the exception even if the frontend requested JSON.The method has been replaced by the two distinct methods
renderHttpException
andconvertExceptionToArray
.The former is called by the Laravel framework for HTML responses, the latter for JSON responses. Note, that
renderHttpException
is actually a misnomer, it should be namedrenderHTMLException
(because JSON also uses HTTP as a transport layer), but heh, this is Laravel. 😢Logging
Exceptions are now properly logged to our own home-brewed logging mechanism. The method
report
of the Laravel default exception handler has been overridden. The Larvavel default method tries to use a service which realizes the\Psr\Log\LoggerInterface
which is not implemented by our classLogs
and hence it has not worked until now.This gives us two benefits:
Consistent Handling of Requests
Previously, incoming requests have been handled very inconsistently. In particular, proper and complete input validation of was matter of luck and even if it partially happened it was too late most of the time. Authorization was inconsistent, too. Some of it happened inside the middleware (but at a too early stage), some of it happened inside the controller methods or even deeper down.
Now, there is a strict order
422 Unprocessable
)401 Unauthorized
)404 Not Found
)403 Forbidden
)Or in cat format:
This gives us three benefits:
All this is achieved by a lot of custom request classes in
App\Http\Requests
. Maybe you know the Laravel feature that routes can have place-holders for model IDs, e.g. something likeGET /album/{album}/
and then "by magic" Laravel hydrates the model and puts it into the request object. This does not work in out case, because our routes don't follow this pattern and the IDs are part of the JSON payload of the request. The custom request classes rectify this problem.Middleware Cleanup
The middleware has been mucked out:
on the expectation of the request (either HTML or JSON)
Miscellaneous
Albums::get
the tags albums have been split from the collection of built-in smart albums. Using two separate collections avoids some awkward and unnecessary checks in the frontend.