Why do I get a "Invalid JSON" when Status Code 204? - laravel

I don't know if it is intended or not. But when I use a Reponse Code 204 in my Controller I get "Invalid JSON was returned from the route" in PHPunit.
But if I change the Response Code to 201, everything works fine... Why?
protected function output($title, $status = 201)
{
return response([
'title' => $title,
'offers' => $this->popular,
], $status);
}
$this->output('Empty', 204); //Here is the error
In my test
/** #test */
public function popular_can_fetch_food_lists() {
$result = $this->json('post', route('popular', [
'type' => $this->food->type,
'id' => $this->food->id
]))->json();
}

If you take a look at https://httpstatuses.com/204 you can read:
A 204 response is terminated by the first empty line after the header fields because it cannot contain a message body.
So in case you are using 204 for anything in fact no content will be in response.
So you shouldn't probably run ->json() in your test. You should only verify if status code is 204

Related

How To Get Relevant Error Data Message From Failed Validation And Failed Guzzle Call In Laravel

I have an api call being made in Laravel using Guzzle/Http. When an error happens I catch it in a try/catch block. I then get the message, but the problem is, I don't want this ugly message that comes back but rather the actual nested message within the $e exception variable. The problem is that the getMessage() method returns a long string from Guzzle.
Error String from $e->getMessage().
"""
Client error: `POST http://mywebApp.com/api/users` resulted in a `422 Unprocessable Entity` response: \n
{"message":"The given data was invalid.","errors":{"email":["This email is not unique"]}}]\n
"""
All I want from this string is:
This email is not unique
The API call
use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\RequestException;
try {
$client->request('POST', 'http://mywebApp.com/users', [
'name' => 'John Doe',
'email' => 'nonuniqueemail#test.com',
]);
} catch (RequestException $e) {
$test = $e->getMessage();
dd($test) //The long message from above
}
If you look closely, the response body is actually a json and can be converted into an array containing the message and an array of errors. You can call json_decode($data, true) and you should get an associative array of the response. In your case something like this should work.
$response = $client->request('POST', 'http://mywebApp.com/users', [
'name' => 'John Doe',
'email' => 'nonuniqueemail#test.com',
]);
$bodyData = $response->getBody();
$errors = json_decode($bodyData, true)['errors'];

How to generate response like below in Laravel 5.8

This is the response which i'm getting if my ajax request ends up with validation errors...
{"readyState":4,"responseText":"{\"email\":[\"The email field is required.\"]}","responseJSON":{"email":["The email field is required."]},"status":422,"statusText":"Unprocessable Entity"}
Want to generate response in similar format, if request processed successfully.
Would appreciate detailed description.
Went through various answers already posted here, but none of them help me out.
Thanks :)
Since this is just a JSON and laravel supports JSON resonses out of the box the only thing you have to do is to return an array with your data:
Route::get("/test", function() {
$response = ["email" => ["The email field is required."]];
return [
"readyState" => 4,
"responseText" => json_encode($response),
"responseJSON" => $response,
"status" => 422,
"statusText" => "Unprocessable Entity"
];
});
Laravel returns every array as JSON and adds the Content-Type: application/json header to the HTTP request.
The example above gives you exactly your string: {"readyState":4,"responseText":"{\"email\":[\"The email field is required.\"]}","responseJSON":{"email":["The email field is required."]},"status":422,"statusText":"Unprocessable Entity"}
EDIT:
To set the status code you can use response()->json() like so:
Route::get("/test", function() {
$response = ["email" => ["The email field is required."]];
return response()->json($response, 422);
});
response() returns an instance of Illuminate\Routing\ResponseFactory so you can lookup the possible parameters: function json($data = [], $status = 200, array $headers = [], $options = 0)

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.

Better way for testing validation errors

I'm testing a form where user must introduce some text between let's say 100 and 500 characters.
I use to emulate the user input:
$this->actingAs($user)
->visit('myweb/create')
->type($this->faker->text(1000),'description')
->press('Save')
->see('greater than');
Here I'm looking for the greater than piece of text in the response... It depends on the translation specified for that validation error.
How could do the same test without having to depend on the text of the validation error and do it depending only on the error itself?
Controller:
public function store(Request $request)
{
$success = doStuff($request);
if ($success){
Flash::success('Created');
} else {
Flash::error('Fail');
}
return Redirect::back():
}
dd(Session::all()):
`array:3 [
"_token" => "ONoTlU2w7Ii2Npbr27dH5WSXolw6qpQncavQn72e"
"_sf2_meta" => array:3 [
"u" => 1453141086
"c" => 1453141086
"l" => "0"
]
"flash" => array:2 [
"old" => []
"new" => []
]
]
you can do it like so -
$this->assertSessionHas('flash_notification.level', 'danger'); if you are looking for a particular error or success key.
or use
$this->assertSessionHasErrors();
I think there is more clear way to get an exact error message from session.
/** #var ViewErrorBag $errors */
$errors = request()->session()->get('errors');
/** #var array $messages */
$messages = $errors->getBag('default')->getMessages();
$emailErrorMessage = array_shift($messages['email']);
$this->assertEquals('Already in use', $emailErrorMessage);
Pre-requirements: code was tested on Laravel Framework 5.5.14
get the MessageBag object from from session erros and get all the validation error names using $errors->get('name')
$errors = session('errors');
$this->assertSessionHasErrors();
$this->assertEquals($errors->get('name')[0],"The title field is required.");
This works for Laravel 5 +
Your test doesn't have a post call. Here is an example using Jeffery Way's flash package
Controller:
public function store(Request $request, Post $post)
{
$post->fill($request->all());
$post->user_id = $request->user()->id;
$created = false;
try {
$created = $post->save();
} catch (ValidationException $e) {
flash()->error($e->getErrors()->all());
}
if ($created) {
flash()->success('New post has been created.');
}
return back();
}
Test:
public function testStoreSuccess()
{
$data = [
'title' => 'A dog is fit',
'status' => 'active',
'excerpt' => 'Farm dog',
'content' => 'blah blah blah',
];
$this->call('POST', 'post', $data);
$this->assertTrue(Post::where($data)->exists());
$this->assertResponseStatus(302);
$this->assertSessionHas('flash_notification.level', 'success');
$this->assertSessionHas('flash_notification.message', 'New post has been created.');
}
try to split your tests into units, say if you testing a controller function
you may catch valication exception, like so:
} catch (ValidationException $ex) {
if it was generated manually, this is how it should be generated:
throw ValidationException::withMessages([
'abc' => ['my message'],
])->status(400);
you can assert it liks so
$this->assertSame('my message', $ex->errors()['abc'][0]);
if you cannot catch it, but prefer testing routs like so:
$response = $this->json('POST', route('user-post'), [
'name' => $faker->name,
'email' => $faker->email,
]);
then you use $response to assert that the validation has happened, like so
$this->assertSame($response->errors->{'name'}[0], 'The name field is required.');
PS
in the example I used
$faker = \Faker\Factory::create();
ValidationException is used liks this
use Illuminate\Validation\ValidationException;
just remind you that you don't have to generate exceptions manually, use validate method for common cases:
$request->validate(['name' => [
'required',
],
]);
my current laravel version is 5.7

Resources