I noticed that when throwing exceptions other then slim’s http*Exception, the response code is always 500. For example, throw new ErrorException('Custom exception', 402); will render the error page with correct details:
@odan, for explaining! I played a bit with re-throwing exceptions manually such as
<?php
public function read(Request $request, Response $response, array $args = []): Response
{
$auth = new Auth($this->db);
try {
$users = $auth->user_read($args['uid']);
} catch (Throwable $e) {
if ($e->getCode() == 450) { throw new HttpNotFoundException($request, 'User not found.'); }
if ($e->getCode() == 550) { throw new HttpBadRequestException($request, 'Wrong user id.'); }
else { throw new HttpBadRequestException($request, 'Something went wrong, sorry.'); }
}
which basically replaces a generic exception (i.e. a validation exception in user_read() function) with a friendlier and more relevant explanation which may change depending on where user_read() is used. I.e.:
when loading information about an authenticated user on each page load, i’d return a 401/unauthenticated if validation fails in user_read()
when showing a user’s profile, the user_read() would return a 404/not found on invalid input.
I suppose that writing my own exception handler is the only way to go here, which is what I totally wanted to avoid (not sure my skill set is up to par to do this correctly / optimally).
Since I didn’t find any prior art that would ease me into writing my own exception handler, it seems that hardly anyone has a need for it. This could mean, that I’m missing out important bits on how to make things simple and I’m over-engineering. Am I? Or does my use case have its validity?
You should distinguish between infrastructure specific exceptions (e.g. HttpNotFoundException, HttpBadRequestException) and domain specific exceptions (e.g. DomainException, ValidationException, etc…).
To map your domain specific exceptions into HTTP specific exceptions you could implement a middleware that catches your domain exceptions and re-throws them as a HTTP exception. Then you don’t have to create the mapping for each route.