Send custom response from validator in Laravel - php

I have a register user route which takes name , email and password. It works perfectly fine if the data is correct i.e. unique email and params are present, but if the user is already registered then Laravel sends auto error message in its own format. I want return format to be consistent in case of success or failure.
Successful Register return data:
{
"status": "success",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjUsImlzcyI6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMC9hcGkvYXV0aC9yZWdpc3RlciIsImlhdCI6MTUyMTI3NTc5MiwiZXhwIjoxNTIxMjc5MzkyLCJuYmYiOjE1MjEyNzU3OTIsImp0aSI6Ik1wSzJSYmZYU1dobU5UR0gifQ.fdajaDooBTwP-GRlFmAu1gtC7_3U4ygD1TSBIqdPHf0"
}
But in case of error it sends data in other format.
{"message":"The given data was invalid.","errors":{"email":["The email has already been taken."]}}
I want both of them to be consistent. Success return data is fine. But i want to customize data if failure occurs. Something like this:
{"status":"error","message":"The given data was invalid.","errors":{"email":["The email has already been taken."]}}
Basically, I need status param to be coming with every response.
Also, I had one query while using Postman the output was pure HTML when error occurred the HTML page was default Laravel Page on the other hand when angular sends the same request the error is json format which i just pasted above.
Since angular is getting JSON respose in any case it is fine for me. But why didn't postman showed me that response.
Register Controller:
public function register(RegisterRequest $request)
{
$newUser = $this->user->create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'password' => bcrypt($request->get('password'))
]);
if (!$newUser) {
return response()->json(['status'=>'error','message'=>'failed_to_create_new_user'], 500);
}
return response()->json([
'status' => 'success',
'token' => $this->jwtauth->fromUser($newUser)
]);
}
Register Request Handler:
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name' => 'required',
'email' => 'required | email | unique:users,email',
'password' => 'required'
];
}

If I understand you correctly, you always get the error-response without the 'status' key.
What happens with your current code, are a couple of things:
RegisterController#register(RegisterRequest $request) is called by a route
Laravel sees you use the RegisterRequest class as an argument, and will instantiate this class for you.
Instantiating this class means it will directly validates the rules.
If the rules are not met, laravel directly responds with the errors found.
This response will always be in laravel's default 'layout' and the code stops there.
Conclusion: Your code is not even triggered when your validation rules are not met.
I've looked into a solution and came up with this:
public function register(Illuminate\Http\Request $request)
{
//Define your validation rules here.
$rules = [
'name' => 'required',
'email' => 'required | email | unique:users,email',
'password' => 'required'
];
//Create a validator, unlike $this->validate(), this does not automatically redirect on failure, leaving the final control to you :)
$validated = Illuminate\Support\Facades\Validator::make($request->all(), $rules);
//Check if the validation failed, return your custom formatted code here.
if($validated->fails())
{
return response()->json(['status' => 'error', 'messages' => 'The given data was invalid.', 'errors' => $validated->errors()]);
}
//If not failed, the code will reach here
$newUser = $this->user->create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'password' => bcrypt($request->get('password'))
]);
//This would be your own error response, not linked to validation
if (!$newUser) {
return response()->json(['status'=>'error','message'=>'failed_to_create_new_user'], 500);
}
//All went well
return response()->json([
'status' => 'success',
'token' => $this->jwtauth->fromUser($newUser)
]);
}
Now, not conforming your validation rules still triggers an error, but your error, and not laravel's built-in error :)
I hope it helps!

In Laravel 8 I added my custom invalidJson with the "success": false:
in app/Exceptions/Handler.php:
/**
* Convert a validation exception into a JSON response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Validation\ValidationException $exception
* #return \Illuminate\Http\JsonResponse
*/
protected function invalidJson($request, ValidationException $exception)
{
return response()->json([
'success' => false,
'message' => $exception->getMessage(),
'errors' => $exception->errors(),
], $exception->status);
}

This is what i came up with:
function validate(array $rules)
{
$validator = Validator::make(request()->all(), $rules);
$errors = (new \Illuminate\Validation\ValidationException($validator))->errors();
if ($validator->fails()) {
throw new \Illuminate\Http\Exceptions\HttpResponseException(response()->json(
[
'status' => false,
'message' => "Some fields are missing!",
'error_code' => 1,
'errors' => $errors,
], \Illuminate\Http\JsonResponse::HTTP_UNPROCESSABLE_ENTITY));
}
}
Create a helper directory (App\Helpers) and add it into a file. don't forget to add that into your composer.json
"autoload": {
"files": [
"app/Helpers/system.php",
],
},
Now you can call validate() in your controllers and get what you want:
validate([
'email' => 'required|email',
'password' => 'required|min:6|max:32',
'remember' => 'nullable|boolean',
'captcha' => 'prod_required|hcaptcha',
]);

Related

Return JSON details of failed validation with Laravel 8

I'm creating an endpoint to store an Office with two fields: name, address.
When validation fails laravel returns status 200 and a welcome page. It should return 4xx and error details with JSON, shouldn't it? I tried to catch an exception (ValidationError) but I don't get the error details.
public function store(Request $request)
{
$request->validate([
'name' => 'required',
'address' => 'required'
]);
// if validation failed, 4xx?
// logic to create a model here
return $office; // everything fine, 201 and object details
}
I'm testing it with unit test and postman:
public function testValidationFailed()
{
$payload = [
"wrongfield" => "Example Name"
];
$response = $this->postJson('/api/offices/', $payload);
and with postman the content-type is application/json
EDITED
Postman was messing up the headers. httpie and curl get the correct response with this code and the accepted answer's.
You can use Validator instead like so
$validator = Validator::make($request->all(), [
'name' => 'required',
'address' => 'required'
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 404);
}
Or you can use validator() helper method
validator($request->all(), [
'name' => 'required',
'address' => 'required'
])->validate();
This will automatically validate and response back with errors and it also, works with web and api endpoints.

How to display custom vaildation error messages for API requests?

try{
$request->validate([
'aadhar' => 'required|digits:12|numeric',
'name' => 'required|string|max:511',
'dob' => 'required|date_format:Y-m-d',
'email' => 'required|email|max:255',
'address' => 'required|string',
'insuranceid' => 'required|digits_between:1,15|integer',
'password' => 'required|min:59|max:60',
]);
}
catch(Exception $error){
$message = $error->getMessage();
$status_code=400;
return response()->json(["message" => $message,"status_code" => $status_code]);
}
This is my piece of code for the validation of the request parameters sent to an API. The documentation gives details only about custom error messages in case of a form request.
The validation errors give the default message "The given data was invalid" but I would like to know which of the parameter was invalid. How do I give custom validation error messages for API requests validation?
First of all, to decouple your code, you could use a Form Request class. From the docs:
For more complex validation scenarios, you may wish to create a "form
request". Form requests are custom request classes that contain
validation logic.
This class contains two methods:
1 - rules, the place where you specify your rules, it should return an array of rules.
2 - authorize that return a boolean,this method control who is allowed to perform this request. By default is set to false, so every call will be rejected.
So, in your case, it should be something like this:
First, create your custom Request class executing this artisan command in your console:
php artisan make:request CreateCustomObjectRequest
This wil create a new class under app/Http/Requests:
class CreateCustomObjectRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
// Implement here your Auth validation, something like:
return auth()->check();
// or just return "true" if you want to take care of this anywhere else.
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'aadhar' => 'required|digits:12|numeric',
'name' => 'required|string|max:511',
'dob' => 'required|date_format:Y-m-d',
'email' => 'required|email|max:255',
'address' => 'required|string',
'insuranceid' => 'required|digits_between:1,15|integer',
'password' => 'required|min:59|max:60',
];
}
}
And then, in your controller, instead of inject a regular Request object, we are gonna use this custom Request object:
use App\Http\Requests\CreateCustomObjectRequest;
// ...
public function store(CreateCustomObjectRequest $request)
{
// the rest of your controller logic.
}
Now, the part you are really interested in. To return errors in a json way you should add the next header when making a request:
Accept: Application/json
This header will tell Laravel that the output should be a json response, so it will convert it to json. Note that this will only work with the validation rules and when returning objects like return $someObject. To more further customization you shoud use something like:
return response()->json(['data' => $someObject], 200);
$validator = Validator::make($request->all(), [
'password' => [
'required',
'confirmed',
'between:8,55'
]
]);
if ( $validator->fails() ) {
return response()->json( [ 'errors' => $validator->errors() ], 400 );
}

REST API, Laravel, Validation

I have a small question. I create simple API using Laravel. When I use validation and if it fails, I got a common message:
{
"result": false,
"message": "The given data failed to pass validation.",
"details": []
}
But how can I get details about which field fails and why like that:
{
"result":false,
"message":"The given data failed to pass validation.",
"details":{
"email":[
"The email field is required."
],
"password":[
"The password must be at least 3 characters."
]
}
}
My code in controller looks like this:
protected function validator(array $data)
{
$validator = Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:3',
]);
return $validator;
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'role_id' => 2
]);
}
It is better to handle the validator within the same process, like this:
public function register(Request $request){
$validator = Validator::make($request->all(),[
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
if($validator->fails()){
return response()->json([
"error" => 'validation_error',
"message" => $validator->errors(),
], 422);
}
$request->merge(['password' => Hash::make($request->password)]);
try{
$user = User::create($request->all());
return response()->json(['status','registered successfully'],200);
}
catch(Exception $e){
return response()->json([
"error" => "could_not_register",
"message" => "Unable to register user"
], 400);
}
}
You should make sure you're sending the request with the Accept: application/json header.
Without that - Laravel won't detect that it's an API request,
If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.
check the documentation
I used validate in my project:
1.I created app/http/requests/CreateUserRequestForm.php
public function rules()
{
return [
"name" => 'required',
"address" => 'required',
"phnumber" => 'required|numeric',
];
}
public function messages()
{
return [
'name.required' => 'Please Enter Name',
'addresss.required' => 'Please Enter Address',
'phnumber.required' => 'Please Enter PhNumber'
];
}
call the RequestForm in controller
use App\Http\Requests\CreateUserRequestForm;
public function createUser(CreateUserRequestForm $request)
{
// create
$user= UserModel::create([
'name' => $request->input('name'),
'address' => $request->input('address'),
'phnumber' => $request->input('phnumber')
]);
return response()->json(['User' => $user]);
}
Try this i didn't try but it should be work for you.
You may use the withValidator method. This method receives the fully
constructed validator, allowing you to call any of its methods before
the validation rules are actually evaluated.
take reference from here. laravel validation
/**
* Configure the validator instance.
*
* #param \Illuminate\Validation\Validator $validator
* #return void
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('email', 'Please enter valid email id');
}
});
}
Try this:
public function create(){
// ------ Validate -----
$this->vallidate($request,[
'enter code here`name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:3'
]);
// ------ Create user -----
$user = User::create(['name' => $request->name']);
return response()->json([
'message' => "success",
'data' => $user``
]);
}

Laravel 5.3: Form Validation does not show errors

I am trying to create a register form using Laravel.
I created a request \App\Http\Requests\RegisterNewUserRequest. This is the rules() method:
public function rules()
{
return [
'email' => 'required|email|min:6|max:255|unique:members',
'password' => 'required|min:6|max:16|confirmed',
'name' => 'required|min:2|max:255|alpha',
'surname' => 'required|min:2|max:255|alpha',
];
}
This is my Membership Controller
//Route::post('{locale}/create-new-user, 'MemberController#create_new_user');
public function create_new_user($locale, RegisterNewUserRequest $request)
{
$input = $request->all();
$input['ip'] = Request::ip();
Member::create($input);
return redirect("/$locale/create-account");
}
I violate validation rules while entering information. For example, I enter two different passwords, and enter 1 character to the email field. It does not say anything about it. Also It does not save the data into database. The page is redirected to same empty register form again.
I have got a validation summary in my view
...
#foreach($errors->all() as $e)
<li>{{$e}}</li>
#endforeach
...
I trace data posted. It is posted. But something is weird in flow.
EDIT
This happens also in my contact form. If I fill the contact form properly, it sends an e-mail. But if I violate the validation rules, it doesn't say anything about validation and not send e-mail.
This is my Contact page controller
/**
* #param Requests\SendMessageRequest $
*/
public function sendMessage($locale, SendMessageRequest $request)
{
// validation
// $this->validate($request,
// ['name' => 'required|min:3',
// 'email' => 'required|min:5',
// 'subject' => 'required|min:3',
// 'message' => 'required|min:15'
// ]);
$formData = $request->all();
$formData['ip'] = Request::ip();
Mail::send('contact.send', compact('formData'),
function ($message) use ($formData) {
$message->from('frommail#example.com', 'Example')
->bcc(['bccmail#example.com'])
->to('tomail#example.com')
->subject($formData['subject']);
}
);
}
\App\Http\Requests\SendMessageRequest file's rules() method
public function rules()
{
return [
'name' => 'required|min:3',
'email' => 'required|min:5',
'subject' => 'required|min:3',
'message' => 'required|min:15',
];
}
Actually it was working before. After adding multi language feature validation did not worked. I do not know if there is any relation.
I suspect about use statements at the top of controllers and requests.

Validation - Returning the first error for an AJAX response

I'm probably over complicating things where it's not needed, but I have a form that is submitted via AJAX and then validated. I'm trying to return the first error that occurs, but I can only ever seem to fetch validation.required which of course is useless to me; I can't tell the user which field failed and nor can I translate an appropriate error message.
What gives? How can I fetch a human-friendly error message and return it back as a JSON response?
Here's what I've got going so far... The biggest how-do-I is fetching the attribute name for the error message, but maybe there's a far easier way to manage this altogether...
/**
* Adds a new post to the system
*
* #param Request $request
* #return json
*/
public function store(Request $request)
{
// run validation
if(!empty($validator = $this->validate($request)))
{
return response()->json(trans('global/'.$validator->first(), ['attribute' => 'how to get attribute?!?!']), 400);
}
// some other logic...
}
/**
* Validates a post add or post update request
*
* #access private
* #param Request $request
* #return mixed
*/
private function validate(Request $request)
{
// set-up validation rules
$validator = Validator::make($request->all(), [
'username' => 'max:255|required',
'email' => 'email',
'service' => 'numeric|required',
]);
$validator->setAttributeNames([
'username' => "Username",
'service' => "Service",
]);
// run validation
if($validator->fails())
{
return $validator->errors();
}
return true;
}
Use code above in your controller store method and do not override validate method:
$rules = [
'username' => 'max:255|required',
'email' => 'email',
'service' => 'numeric|required',
];
$attributes = [
'username' => "Username",
'service' => "Service",
];
$this->validate($request, $rules, [], $attributes);
It will automatically returns errors json if validation fails.
Errors example:
{"username":["The Username field is required."],"service":["The Service field is required."]}

Categories