How to generate response like below in Laravel 5.8 - ajax

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)

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'];

axios post to find out if data is valid

OK I am at the end of my day and I am not thinking straight. So this is what I have...
a Laravel controller, send it a username, and it tells me if the username is available, and if it isnt, it gives me a 422 code
public function checkUsername(Request $request) {
Validator::make($request->all(), [
'name' => ['required', 'string', 'max:255', 'unique:users'],
])->validate();
return response()->json([
'valid' => true,
'data' => [
'message' => 'Username is available!'
]
], 200);
}
Example of valid response:
{"valid":true,"data":{"message":"Username is available!"}}%
The curl to test is:
curl -X POST -H "Content-Type: application/json" -d '{"name": "bossryan"}' http://127.0.0.1:8000/api/checkusername
Next: I have a frontend Vue using Vee-validate. It does a bunch of things, but I need to add this latest validation into the mix, so if the username is taken (I don't get the valid response from above, it needs to reply with "This username is already taken"
validateUsername(value) {
// if the field is empty
if (!value) {
return 'This field is required';
}
const regex = /^[a-zA-Z0-9_.+-]{4,20}$/i;
if (!regex.test(value)) {
return 'This must be a minimum of 4 characters';
}
return true;
},
This is the axios I created but it isnt working:
const isUnique = (value) => {
return axios.post('/api/checkusername', { email: value }).then((response) => {
// Notice that we return an object containing both a valid property and a data property.
return {
valid: response.data.valid,
data: {
message: response.data.message
}
};
});
};
I know I need to add in axios, but I am just having a heck of a time setting it up and my mind keeps rushing around. I am just looking for someone who can just help me plug in the axios request above //All is good, so I can finish this up.
THANKS FOR THE HELP COMMUNITY!
Vee-validate seems to want a resolved promise for async validation. Axios will reject the promise if the status is >= 400 so you need to handle that accordingly.
Assuming when validation fails that the response body matches the same { valid, data: { message } } format, you'd want something like the following
const isUnique = (name) =>
axios.post("/api/checkusername", { name })
.then(({ data }) => data)
.catch(err => ({ // resolve with error details
valid: err.response?.data?.valid ?? false,
data: {
// get the message from the response if it exists
message: err.response?.data?.data?.message ?? "Validation failed"
}
}));
export default {
methods: {
async validateUsername(value) {
// do your synchronous checks as per question
const check = await isUnique(value);
return check.valid || check.data.message;
}
}
}
This will provide a generic message "Validation failed" if the 422 response body doesn't match expectations.

Paypal API Subscription Create returns 400 Bad Request response "name", though request is formed as in documentation

I try to implement Paypal subscription service according to: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create
This is my first try.
In sandbox business account I have created two test subscriptions: monthly and yearly and configured application with their id's.
This is the method:
public function createSubscription($planSlug, $name, $email) {
return $this->makeRequest(
'POST',
'v1/billing/subscriptions',
[],
[
'plan_id' => $this->plans[$planSlug],
'subscriber' => [
'name' => [
'given_name' => $name,
],
'email_address' => $email
],
'application_context'=> [
'brand_name' => config('app.name'),
'shipping_preference' => 'NO_SHIPPING',
'user_action' => 'SUBSCRIBE_NOW',
'return_url' => route('subscribe.approval', ['plan' => $planSlug]),
'cancel_url'=> route('subscribe.cancelled')
],
],
[],
$isJsonRequest = true
);
}
However, when I make a call to API, to create a test subscription, I get weird response that 'name' parameter is formed incorrectly:
php artisan tinker
>>> $paypal = resolve(App\Services\PaypalService::class);
=> App\Services\PaypalService {#3413}
>>> $paypal->createSubscription('monthly', 'Test', 'test#test.com');
GuzzleHttp\Exception\ClientException with message 'Client error: `POST https://api-
m.sandbox.paypal.com/v1/billing/subscriptions` resulted in a `400 Bad Request` response:
{"name":"INVALID_REQUEST","message":"Request is not well-formed, syntactically incorrect, or
violates schema.","debug_id (truncated...)
This is strange, because in Paypal API doc (see above), the 'name' param is described exactly like that!
Do I miss something or it is Paypal API is acting funky?
try this :
try {
// your code here
} catch(\Throwable $th) {
if ($th instanceof ClientException) {
$r = $th->getResponse();
$responseBodyAsString = json_decode($r->getBody()->getContents());
dd($responseBodyAsString);
}
}
I've faced this too before and it was not easy for me to figure out how to show an explicit error message.
'return_url' => route('subscribe.approval', ['plan' => $planSlug]),
'cancel_url'=> route('subscribe.cancelled')
the problem is in this two url, may be you have changed the APP_URL in the .env
APP_URL=http://127.0.0.1:8000
put app url that and try

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

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

Validate POST request Laravel?

I validate POST request like:
$validator = Validator::make($request->all(), [
"id.*" => 'required|integer'
]);
if ($validator->fails()) {
return response()->json($validator->errors, 400);
}
echo "Ok";
When I send request without parameter id it skips validation and returns echo "Ok";.
Why validation does not work?
If you expect id is array of integers you should update validation rules like this:
$validator = Validator::make($request->all(), [
"id" => 'required|array',
"id.*" => 'integer'
]);
First know when using $request->validate(); when it fail exceptions are raised!
And they are automatically handled by laravel. If it's a get, or a normal post form, then the process will redirect back to the form.
To note, when using the validate method during an AJAX request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code.
If you want to not have such an automatic behavior, create manually a validator, then you can check with ->fails() method, as the example show bellow. That's can be handful in a lot of situations.
<?php
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Store the blog post...
}
}
And there is no better then the doc itself: https://laravel.com/docs/5.7/validation
there is a lot of options, to personalize our validation process.

Resources