change the format of laravel API Errors and Exceptions - laravel

I write api for the applications with laravel.
for example I have two field which are "city_id" and "address" and I validate them with these rules :
$request->validate([
'city_id' => 'bail|required|numeric',
'address' => 'required'
]);
if validation fails the response will be :
{
"message": "The given data was invalid.",
"errors": {
"city_id": ["The city id field is required."]
}
}
everything is fine but I want change the validation error at api response to this :
{
"msg" => 'The city id field is required.'
}
actually I want send one error without the key . where can I change that?

You can write/modify the Laravel exceptions handler. Here you can read more about it here https://laravel.com/docs/5.8/errors or here https://laravel.com/docs/6.x/errors (depends what version of Laravel you using)
Example:
class Handler extends ExceptionHandler
{
...
public function render($request, Exception $e)
{
if ($exception instanceof ValidationException) {
return new JsonResponse(
['msg' => $e->getMessage()],
400
);
}
}
...
}

Related

Changing default error message when record not found - Laravel API

I am building a simple API, which there is a point that when the ID entered in the endpoint URL does not point to a valid record, I get a standard NotFoundHttpException. And I cannot figure out how to override this in order to provide my own error message response as I do not wish to share my Model name etc.
Endpoint
Route::get('{mrl}', [MrlController::class, 'show']);
Controller
public function show(Mrl $mrl)
{
if ($data = $mrl) {
return response(['status' => 'ok', 'data' => $data], 200);
} else {
return response(['status' => 'error', 'message' => 'Could not retrieve data'], 500);
}
}
When I run this when a record exists I receive the following which is what I expect.
{
"status": "ok",
"data": {
"id": 98,
"market_id": 1,
"crop_id": 2,
"chemical_id": 113,
"maximum_residue_level": null,
"exempt": 0,
"comments": null,
"date_verified": "2021-10-07",
"created_at": "2021-10-19T05:42:12.000000Z",
"updated_at": "2021-10-19T05:42:12.000000Z"
}
}
However, when I enter an ID in the route endpoint for a record that does not exist, I receive the following:
{
"message": "No query results for model [App\\Seasonal\\Mrl] 99"
}
This is happening from what I understand to be the auto find of the record between the Route and the controller, and I am lost as to how to customize this behavior.
Thanks in advance for your help!
you don't show the code where you are fetchning the model from the database but we can assume something like that:
$mrl = Mrl::findOrFail($id);
show($mrl);
The model findOrFail() method throws an exception when the model is not found, which is convenient when you want to adapt the response.
You can imagine something like that:
try {
$mrl = Mrl::findOrFail($id);
return response(['status' => 'ok', 'data' => $data], 200);
} catch (ModelNotFoundException $e) {
return response(['status' => 'error', 'message' => 'Could not retrieve data'], 500);
}
The idea is to catch the error thrown by your model to change the message and status code of the response.
When building APIs you should event add a "generic" catch statement for any unhandled errors to display a standardized generic error message and log what happened, like this:
try {
$mrl = Mrl::findOrFail($id);
// Do more things that could generate errors ?
return response(['status' => 'ok', 'data' => $data], 200);
} catch (ModelNotFoundException $e) {
// Not found
return response(['status' => 'error', 'message' => 'Could not retrieve data'], 500);
} catch (\Exception $e) {
// Generic error
\Log::error($e);
return response(['status' => 'error', 'message' => 'An error occured'], 500);
}
After spending time reading the docs I finally found the answer on how to customize the behaviour if a record is found when utilising Eloquents Route Model Binding.
In the Laravel docs, there is a method for this explicit purpose, to override the default behaviour when the bound model does not have a valid record.
The method for doing this is to chain the ->missing() function call to the end of your route and then providing the behaviour for the response you wish to provide. As below:
Route::get('{mrl}', [MrlController::class, 'show'])->missing(function () {
return response(['status' => 'error', 'message' => 'Invalid query.'], 404);
});
Chaining this method onto my request has enabled me to return the generic response I was hoping for when querying an invalid model record.

How to custom message validator with Laravel

I am making an API that allows users to input some information like email, phone number, address ...
But if users input wrong phone nums, the validate error is
{
"message": "The given data was invalid.",
"errors": {
"phone": [
"The phone has already been taken."
]
}
}
As you can see the message returns is
"message": "The given data was invalid."
. But the message I expect is The phone has already been taken. How can I custom the message as I expect? With an email validator, the message is the same but the key is email. The message I expect is
"message": "The ... has already been taken. "
I'm using laravel 8 and validate in Request
Example a function rules()
public function rules()
{
return [
'profile_img' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:' . config('filesystems.max_upload_size'),
'name' => 'nullable|min:3',
'phone' => [
'required',
'numeric',
new UpdatePhoneRule(User::TYPE_CLIENT),
],
'email' => [
'nullable',
'email',
new UpdateEmailRule(User::TYPE_CLIENT),
]
];
}
Thanks
On the Request php file you can use this function failedValidation() and pass in a Validator. This way you can alter or customize the response if validation fails.
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
protected function failedValidation(Validator $validator) {
throw new HttpResponseException(response()->json(['status'=>'failed',
'message'=>'unprocessable entity',
'errors'=>$validator->errors()->all()], 422));
}
Sample response is here..
{
"status": "failed",
"message": "unprocessable entity",
"errors": [
"The name must be a string.",
"One or more users is required"
]
}
As you can see the message is changed now you can do whatever you want on the response message.
Also you can try this
$validator->errors()->messages()[keyname]
You have to use unique in your validation
$this->validate(
$request,
[
'email' => 'required|unique:your_model_names',
'phone' => 'required|unique:your_model_names'
],
[
'email.required' => 'Please Provide Your Email Address For Better Communication, Thank You.',
'email.unique' => 'Sorry, This Email Address Is Already Used By Another User. Please Try With Different One, Thank You.',
'phone.required' => 'Your custom message',
'phone.unique' => 'The phone has already been taken'
]
);
You can do this in the lang file: resources/lang/en/validation.php. If all you want to do is change, for example, the message for a unique rule on an email field across the entire app you can do:
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
'email' => [
'unique' => 'This email is already registered...etc',
]
],

Laravel Passport - Change default Unauthenticated response to custom one

Thank you in advance,
I am using laravel passport for API user authentication while if access_token is invalid then we are receiving response like below
{
"message": "Unauthenticated."
}
but I want that response like below
{
"code" : 0,
"message": "Unauthenticated."
"data" : [],
}
If you want to change how Laravel renders an error you can do it in your app/Exceptions/Handler.php class.
In this particular case you can override the unauthenticated method by adding te following to your Handler class:
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json([
'message' => $exception->getMessage(),
'code' => 0,
'data' => [],
], 401);
}
return redirect()->guest($exception->redirectTo() ?? route('login'));
}

Handling error thrown by resource controller's method

I' working with Laravel 5.6 and i've decided to create a resource controller to handle one of my models. Right know im trying to destroy a record from the database like this:
public function destroy(Role $role)
{
$role->delete();
return response([
'alert' => [
'type' => 'success',
'title' => 'Role destroyed!'
]
], 200);
}
It works just fine as longs as the $role exists. My problem is that i want to handle the response myself in the case that $role does not exist to do something like this:
return response([
'alert' => [
'type' => 'ups!',
'title' => 'There is no role with the provided id!'
]
], 400);
But instead, i'm getting a error like this:
"No query results for model [App\\Models\\Role]."
And that is something I don't want.
Thanks in advance!
The "No query results for model [App\\Models\\Role]." is the standard response message for a ModelNotFound exception in Laravel.
The best way to change the response for an exception like this is to use the exception handler's render function to respond with whatever message you want.
For example you could do
if ($e instanceof ModelNotFoundException) {
$response['type'] = "ups!;
$response['message'] = "Could not find what you're looking for";
$response['status'] = Response::HTTP_NOT_FOUND
}
return response()->json(['alert' => $response], $response['status']);
The alternative is to ensure that the ModelNotFound exception does not get thrown (So use ->find() rather than ->findOrFail() when querying the model)
and then using the abort helper like so if no results are returned:
abort(400, 'Role not found');
or
return response(['alert' => [
'type' => 'ups!',
'title' => 'There is no role with the provided id!']
],400);

Laravel : Validation not working when testing on live

Create a user validate function with validation in API integration When i try to validate user with some validation and check it in postman it is working perfect in local machine but when i uploaded code on server and test, it is not working.
Controller function :
public function confirmUser(ForgotPasswordRequest $request)
{
// ForgotPasswordRequest for validate
$user = User::leftjoin('user_details','users.id', '=', 'user_details.user_id')
->where('user_details.phone', '=', $request->post('phone'))
->where('users.email', '=', $request->post('email'))
->first();
if(!$user) {
return response()->json([
'message' => "The credentials you provided cannot be determined to be authentic.!!",
'status_code' => 404,
]);
}
$token = JWTAuth::fromUser($user);
return response()->json([
'message' => "Your account is eligible for change password.!!",
'token' => $token,
'status_code' => 200
]);
}
My forgotPasswordRequest.php code :
<?php
namespace App\Api\V1\Requests;
use Config;
use Dingo\Api\Http\FormRequest;
class ForgotPasswordRequest extends FormRequest
{
public function rules()
{
return Config::get('boilerplate.forgot_password.validation_rules');
}
public function authorize()
{
return true;
}
}
And in config folder boilerplate.php file contain forgot_password rules :
'forgot_password' => [
'validation_rules' => [
'email' => 'required|email',
'phone' => 'required|min:11|numeric'
]
],
When email and phone null it returns only email validation not phone:
{
"error": {
"message": "422 Unprocessable Entity",
"errors": {
"email": [
"The email field is required."
]
},
"status_code": 422
}
}
What i am doing wrong ? It is related to cookies ?
There is nothing going wrong here.
Laravel checks whether the email field is properly filled in. It isn't, Laravel breaks out of the validation method and returns the exception.
If you fill in the email field, but leave the phone field blank, then the validation response will be about the phone field.

Resources