Laravel Dingo API - Only log 500 error, never send error details via api? - laravel

I made the below controller to demonstrate the issue of handling 500 errors I am having with dingo api in laravel. I want to be able to detect when a 500 error will be thrown so it never makes it to the client (as it is too much details to share with the client and they should only be logged by Laravel).
The methodgetUser() returns a 500 error intentionally due to the typo firsgt()
class TestController extends Controller {
public function getUser() {
$data = User::firsgt(); //returns 500 error
return $data;
}
}
This is what the client sees:
In my controllers, I handle errors manually by returning a success/error json response from within the controllers, but if an error occurs that I did not expect, the api returns it and it has too much details for the client to see. Instead, these unexpected errors should bubble up to some sort of handler to return a generic error occurred response. In Laravel, setting APP_DEBUG = false in .env works for laravel (but not for dingo api), this has no effect and the full error is returned to the client. Looking for a safety net for errors that slip through the cracks.
How can we return an error message like 'Error occurred' instead of the too much details for client 'Call to undefined method App\User::firsgt()'?
Note: I don't want to handle it one by one for each controller method, but instead capture any 500 before it is returned to client, and return the custom 500 generic message 'Error occurred'

You should check your dingo config and set these two parameters to false.
APP_DEBUG=false
API_DEBUG=false
If you still encounter the issue, just as suggested in the comment ensure you are in production.
Finally if you are still having the same issues (which by now normally should not exist after setting those fields to false) then you might be interested in checking this issue (date back since 2015).
The fix from one of the comment says (verbatim):
app(\Dingo\Api\Exception\Handler::class)->register(function (\Exception $exception) {
if (!env('API_DEBUG') && !$exception instanceof \Symfony\Component\HttpKernel\Exception\HttpException) {
// Whatever other handling you want goes here..
// Mimic the normal API response with a different message
return \Response::make([
'message' => 'Internal server error',
'status_code' => 500,
], 500);
}
});
Beware that I didnt test this myself.

If you haven't already done yet, publish dingo config file
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
Then, open config/api.php and edit the errorFormat value to whatever message you want.
Replace :message with a generic message

Related

How to use custom error settings for JWT middleware

I have followed the cook books guide to the letter, found here https://echo.labstack.com/cookbook/jwt
But when using the JWT middleware I am having some issues with adding custom error messages. Login works fine, even to the point of not giving details (username & password) that returns a 404.
But when the JWT is missing it returns a 400, I want it to also return a 404.
So in my research I found this, https://forum.labstack.com/t/custom-error-message-in-jwt-middleware/325/3 which lists the following middleware.ErrJWTMissing & middleware.ErrJWTInvalid But is very unclear on how to set these?
I have tried setting them as vars on the router file, like so
var (
ErrJWTInvalid = echo.NewHTTPError(http.StatusTeapot, "test 104")
ErrJWTMissing = echo.NewHTTPError(http.StatusTeapot, "test 103")
)
But the error that sill comes back to me is a 400 and not a 418 (as this is just a test). So what am I doing wrong?
You can change the HTTP code and message this way.
func init() {
middleware.ErrJWTMissing.Code = 401
middleware.ErrJWTMissing.Message = "Unauthorized"
}
First, a point on your statement that you want to return a 400 and also a 404 error - you cannot do this. You're sending one response from the server so it gets exactly one response code. You could send a 207, but we're not really talking about multiple resources here, so don't do that. In my opinion, a 400 error is indeed the correct response for a missing JWT as that constitutes a bad request. A 404 "Not Found" means that the requested resource (the thing on the server side) could not be found. It does not mean that something in the request could not be found.
As for setting your custom error message, you're likely to be out of luck without altering the source code for Echo. That specific response is coming from within the middleware handlers of the package itself (you can see it here). This is mostly abstracted away from you, so without looking at the inner workings of the package, there would be no way to tell where this was coming from, and frankly there's not a lot that you can easily do about it. ErrJWTMissing is indeed the variable that the package uses internally for this error message, but Echo does not appear to provide an exported setter method for you to change this value, so you're stuck with what it is.
If you truly wanted to set a custom error method for this case I think your options would be to:
Write your own middleware to intercept the request before it was handled by Echo's middleware, where you could handle the request however you wanted.
Edit the Echo source to work how you wanted it to work -- specifically, all you would have to do is edit ErrJWTMissing.
Basically, Echo is trying to do you favors by handling all of this middleware processing for you, and it's a lot of work or hackery to un-do that work while still using Echo.

Laravel+Vue. Caching problems (actually, just reserved variable name)

I create Laravel+Vue simple REST API web-app.
In Vue component I have a method with an api request.
I simplified this to see the core of the problem:
phpValidate() {
axios
.post("api/validate", self.programmer)
.then(function(response) {
console.log(response.status);
});
}
In the controller I have a method validateIt(), which handle this "api/validate" request.
It returns:
return array('status' => $status, 'data' => $data);
The $status can be equal to 200 or 422, depends on the input data.
The problem is that from some point, it began to return $status of 200 always.
Even if I delete all the code from the method validateIt() and just leave two lines:
$status = 422;
return array('status' => $status);
I still receive 200.
If I delete the whole method in controller, it gives an Internal Server Error 500.
So, the route and function name is correct.
When I put it back, I can write there whatever I like, it doesn't have any sence - it still returns 200!
If I use debugger, I can see that at the end of validateIt() method it returns 422.
But, when I get the response in phpValidate() I see again 200.
Unbelievable!
I tried:
npm run dev
and
php artisan cache:clear
doesn't help!
Also I tried to restart the server and use different browsers, doesn't help.
Actually, this is not a problem of caching.
It looks like the variable name STATUS is reserved.
Doesn't matter what value you give to $status in the controller method.
The $status always contains the actual status of the request and you can't change it manually. Even if the method is empty it will return $status 200 because the request was sucessfull.
The solution is to use another variable name for your own data.
I had the same problem and to solve it add version where you include your vue frontend file,do it like this it will never cache again:
<script src="{{ asset('js/app.js?version='.date("ymdhis").'') }}"></script>
and you should make sure that your vue server is running use npm run watch or npm run dev

Authentication in Lumen 5.2

In the AuthServiceProvider;
Auth::viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
I can't seem to get this to work. GET requests do not have a body so no input is present.
Also I've tried using $request->header('api_token') but still getting unauthorised
If I do an independent search on the DB like below it works;
Auth::viaRequest('api', function ($request) {
return User::where('api_token', 'my_api_key')->first();
});
Can anyone confirm that the $request header can be accessed here?
Before you read my answer, please read this article (just in case you're using Apache Web Server).
Headers containing invalid characters (including underscores) are now silently dropped.
Ok, now to get your api_token value, you need to change your request header-field name to Api-Token (this is standard, you may read this).
Now you can access your Api-Token value via:
$request->header('Api-Token', 'any-default-value');
If you really want to get your non standard header definition, you may use getallheaders function. For more information, you may read Symfony\Component\HttpFoundation\ServerBag#getHeaders method.

Laravel 5 - Deal with controller validation and RESTful clients like Postman

I am using laravel's RESTful resource routes and controllers.
I testing my api with PostMan and/or RestClient(firefox)
Everything was fine before I added laravel's controller validation. Now it respond with very strange responses like: status code 0, or even executes the code with not valid data. Or even shows strange results taken from the database (which are not included to the controller at all).
That's creepy.
For example:
public function store(Request $request) {
$this->validate($request, [
'room_id' => 'required|integer',
'body' => 'required'
]);
exit; // Stop the process after the validation...
// ... Logic to STORE the MESSAGE in the database ...
return response(null, 204);
}
This store function must only validate the data and respond with an error if validation fails,
But when I execute it from PostMan, It returns response with list of all rooms which belongs to this user. This is creepy, I cannot realize why this is happening.
When I use the jQuery.ajax() method with the same request options, it works fine. It validates the data and stores the message in the database.
Question : Is there a way to deal with postman?
Question : Is PostMan dangerous? Since the server respond with database info (which is not included to the controller responses).
If you take a closer look at laravel's validation document it says that when data is submitted via HTML form,the validate method would redirect you to a previous page and the error would be flashed into the session, if the validation fails. Although, you will be able to access these error messages in your view using the $error variable. Therefore you will need to submit the data via ajax in-order to get the JSON error message.
When validatioin failed, Laravel needs to know you are sending ajax request or you explicitly want json respond, otherwise it redirects you to previous page.
You can check my answer here
https://stackoverflow.com/a/38478362/6246592

If codeigniter model returns 0 rows, display custom error message. How to extend to a general case?

I have a variety of functions within my models which serve a different purpose.
One for example looks up the data for a given $_GET variable in the URL string.
I am trying to work out a way of displaying an error message if there is no matching row in the database due to url string manipulation for example.
My first idea was simply to return an error message (if there is an error) with each call to the function, then simply have an if statement whereby if there is an error, an error view is shown, and if not the normal view is shown..
Problem with this is that this function is called numerous times in my controller, and other similar functions are called throughout my code which need similar error handling..
I dont want millions of similar if/else statements all over my code to handle errors..
Anyone got any better ideas?
Cheers
Use the flashdata item of the session class. You can concatenate error messages and put them in the flashdata item to display.
my_function(){
// code that determines if there is an error returns => $error
if ($error)
{
// concat previous and current errors
$new_error = $this->session->flashdata('errors') . $error;
// replace 'errors' with newly concatenated errors
$this->session->set_flashdata('errors', $new_error);
}
}
This will keep track of all errors generated through each request and allow you to display a "list" of errors for that particular request.
echo $this->session->flashdata('errors');

Resources