I am trying to implement a REST API for basic CRUD operations with Laravel 5.4
I have a post method as below.
public function storeReport(StoreReportRequest $request){
$inputs = $request->only('company_name',
'company_experience',
'service_start_time',
'service_end_time',
'latitude',
'longitude',
'additional_info',
'user_id'
);
//save image content to folder and image name to database
$image = $request->file('picture');
$fileName = $image->getClientOriginalName();
$image->storeAs('images', $fileName);
$inputs['picture_url'] = $fileName;
$report = Report::create($inputs);
return 'success';
}
I do not have any problems with storing the data.I have some rules for validation and i do validation via StoreReportRequest class.What i want to learn is how can i handle the response after POST request. For example if validation fails or any exception occurs what should i return and how it should be. I have done some research but couldnt find any proper answers. Any help would be appreciated.
Well, I normally do a response in json wich is always easy to parse, on the end of your function if everything was correctly done you can do this:
return response()->json(["response" => true]);
and if failed for some reason
return response()->json(["response" => false, errors => ["This can be an array or whatever"] ]);
The validation will return a json response with the error messages with a status code of 422. So you don't have to worry about the validation failure exceptions.
As for what you want to do after creating the report is upto you. You could return a json response with the newly created report or send a success message with the status code 200 (set by default).
Laravel does most of the work for you and you just need to define the responses based on your API requirements.
Related
I have a somewhat simple authentication system using ReactTS as a frontend and Laravel as a backend. What I am looking to do is send errors from the backend to the frontend when I redirect a user. This is the code I currently have:
return redirect('/account')->withErrors('Error here');
I have also tried:
return redirect('/account')->with('message', 'Error here');
But in my React frontend I use a flash manager to handle errors. However I do not know how to get this error or session data in the frontend. Code:
addFlash({ key: 'account', message: 'error here' });
This function just takes a message and shows it to the user with a coloured bar. My issue is I do not know how to get these errors I am redirecting with in the frontend.
As I understand your question you have separated backend and frontend and probably have an API that handles your requests. If this is the case and I understood it correctly then I'll tell you how I handled it and maybe it will help you as well.
In my case, I'm returning an HTTP response no matter if it is for errors or for success and I think that you should do the same, let me show you a simple example to explain it better for you.
Here's my controller (simplified for your concern):
class CreateEventRegistrationController
{
public function __invoke(Request $request)
{
/**
Here you validate your fields or something
specific logic for your project.
*/
$validator = Validator::make($request->all(), [
'firstName' => 'required',
'lastName' => 'required'
]);
/**
If your validation fails then throw an HttpResponseException
*/
if ($validator->fails()) {
throw new HttpResponseException(response()->json([
'errors' => $validator->errors()
]));
}
/**
Store something in database or whatever you need to do in your app
*/
/**
And at last return a successful response
after everything is working as expected.
*/
return response('Created successfully', Response::HTTP_CREATED);
}
And with this logic then I have the messages on my frontend part where I handle there the error messages or success ones.
Hope this helps you, if I couldn't understand your issue properly you can comment it more specifically so I can help you more.
I am using the Microsoft Graph and I need to set up a webhook to receive changes to email and calendar events. I was able to get it working with my PHP Laravel application, but now that I am trying to subscribe to notifications, I am running into issues with validating the notificationUrl, which is pointing to a public server of mine.
The script for creating the webhook is returning the following error:
Client error: POST https://graph.microsoft.com/v1.0/subscriptions resulted in a 400 Bad Request response:
{
"error": {
"code": "InvalidRequest",
"message": "Subscription validation request failed. Response must ex (truncated...)
The truncated part I believe is
Subscription validation request failed. Must respond with 200 OK to this request.
Here is my code for creating the subscription:
$data = [
"changeType" => "created",
"notificationUrl" => "https://anatbanielmethod.successengine.net/office365/webhooks/events",
"resource" => "me/events",
"expirationDateTime" => "2018-12-20T18:23:45.9356913Z",
"clientState" => "secret",
];
$result = $graph->createRequest('POST', '/subscriptions')
->attachBody($data)
->execute();
and here is my method for my notificationUrl:
public function events()
{
//if validationToken exists return that to validate notificationUrl
if(isset($_REQUEST['validationToken'])){
return response($_REQUEST['validationToken'], 200)
->header('Content-Type', 'text/plain');
}
//process event normally for those that have already been validated
}
Once again this URL is public and live and I have tested it by using Postman to send it test posts and it is working fine. Also, I added this route to my VerifyCsrfToken middleware to allow a third party post to hit this URL.
Originally I set up a simple single page PHP script to test validating the notificationUrl and that simple script worked fine. It successfully validates Webhooks created that point to it. Here is that one page script code:
<?php
if(isset($_REQUEST['validationToken'])){
echo $_REQUEST['validationToken']; // needed only once when subscribing
} else {
//process like normal not a validation Token request...
}
}
So I would expect that the Laravel endpoint would work like the simple one page PHP script, and it is when I test both URLs in Postman, but the Laravel endpoint is not validating when Office365 attempts to validate it when creating a new webhook.
I have searched all over for help on this and read through all of the Microsoft developer documentation I can find on webhooks and these are some of the more helpful parts of the documentation but I am still not finding an answer to this issue:
https://learn.microsoft.com/en-us/graph/api/subscription-post-subscriptions?view=graph-rest-1.0
https://learn.microsoft.com/en-us/graph/webhooks#notification-endpoint-validation
Any ideas of this?
Thanks Marc! You were correct about the linefeed being the issue, I am still not sure where the line feed is coming from, some how Laravel appears to be adding it. Needless to say I found a solution by adding an "ob_clean();" right before returning the response. Below is my updated notificationUrl method:
public function events()
{
//if validationToken exists return that to validate notificationUrl
if(isset($_REQUEST['validationToken'])){
ob_clean();//this line is cleaning out that previously added linefeed
return response($_REQUEST['validationToken'], 200)
->header('Content-Type', 'text/plain');
}
//process event normally for those that have already been validated
}
It's odd that JakeD's answer requires the use of ob_clean(). here is my webhook controller method in my Laravel 5.7.x app:
use Illuminate\Http\Request;
public function webhook (Request $request) {
if (filled($request->input('validationToken'))) {
return response($request->input('validationToken'))
->header('Content-Type', 'text/plain');
}
// code to process the webhook after validation is complete
}
I don't see an extra linefeed character and the Microsoft Graph API subscription is validated and created.
If the incoming request was an AJAX request, no redirect will be
generated. Instead, an HTTP response with a 422 status code will be
returned to the browser containing a JSON representation of the
validation errors.
This is not working! I am trying to access the route via an ajax request and it redirects back.
If validation passes, your code will keep executing normally. However, if validation fails, an Illuminate\Contracts\Validation\ValidationException will be thrown. This exception is automatically caught and a redirect is generated to the user's previous location. The validation errors are even automatically flashed to the session!
Now I want to know where does laravel catch this exception so that I can modify it?
This is handled inside the FormRequest class:
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException($this->response(
$this->formatErrors($validator)
));
}
You can override this function in your own Request object and handle a failed validation any way you like.
After been researching for a while I will post my results so anyone with this problem saves a lot of time.
#Faiz, you technically shouldn't change a thing if you want to stick to laravel behavior (I'll always try to follow taylor's recommendations). So, to receive a 422 response code status you need to tell phpunit you will send a XMLHttpRequest. That said, this works on laravel 5
$response = $this->call('POST', $url, [], [], [],
['HTTP_X_Requested-With' => 'XMLHttpRequest']);
More information at Github Issues. Besides, if you look at Symfony\Component\HttpFoundation\Request#isXmlHttpRequest you will find that this header is used by "common JavaScript frameworks" and refers to this link
Haven't tested on the browser yet, but I think this should work too.
I understand how to validate requests by type-hinting the class name in the controller method. However for Ajax requests, According to the documentation, I should validate data in the controller, because using a validator class will redirect rather than send a response.
The main part I'm looking at is this:
If the incoming request was an AJAX request, no redirect will be
generated. Instead, an HTTP response with a 422 status code will be
returned to the browser containing a JSON representation of the validation errors.
However, my controller is as follows:
public function update(App\Permission $permission, Request $request)
{
$this->validate($request, [
'permission_description' => 'required|string'
]);
...
}
And I can't for the life of me get it to respond with JSON. The documentation states that if it fails, it throws an Illuminate\Contracts\Validation\ValidationException exception, but I can't catch it.
Whenever it fails, it always redirects back to the edit page. Obviously I don't want this, I want the json response.
I have just tried "manually writing it out" with the whole $v = Validator::make($request->all(), ...); which does work, but what's the point in using the $this->validate() way if it doesn't work?
Does the $this->validate() method just not work with AJAX and I have to write it the long way each time? Am I doing something wrong?!
Below is what I've tried:
public function update(App\Permission $permission, UpdatePermissionRequest $request)
{
/** Redirects rather than returns JSON if the validation fails **/
}
----------------------------------
public function update(App\Permission $permission, Request $request)
{
$this->validate($request, [
'permission_description' => 'required|string'
]);
/** AND I've also tried: **/
try {
$this->validate($request, ['permission_description' => 'required|string']);
} catch (\Illuminate\Contracts\Validation\ValidationException $e {
echo $e; /** Echoing for debug reasons **/
exit;
}
...
/** Still redirects the browser, even if it is an AJAX request **/
}
-----------------------------------------
use Validator;
...
public function update(App\Permission $permission, Request $request)
{
$v = Validator::make($request->all(), [
'permission_description' => 'required|string'
]);
if($v->fails())
{
return response()->json(['reply' => false]);
}
/** Works **/
}
UPDATE
The documentation is incorrect. It states that the $this->validate() method throws a Illuminate\Contracts\Validation\ValidationException but it doesn't. It throws a Illuminate\Http\Exception\HttpResponseException exception.
Simply telling that you want json in the header should also fix this. Laravel checks if the request is ajax of if json is requested.
if ($this->ajax() || $this->wantsJson())
{
return new JsonResponse($errors, 422);
}
Solution:
Add header
Accept: application/json
Your statement that the docs say it is best to validate AJAX requests in the controller is simply incorrect.
If you scroll a bit further down from what you linked - you'll see this under the FormValidation section
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.
In other words - there is no reason you cannot do this in a simple FormRequest and simply your code significantly. It will automatically handle the fact it is an AJAX call and return the appropriate HTTP responses.
I do this all the time in my L5 apps - it works flawlessly.
Ok, so it looks like there were 2 contributing factors.
The reason it was redirecting instead of responding with JSON is because the X-Requested-With field wasn't set on the AJAX request. This the AJAX wrapper for the application was setup to deal with Cross Domain requests, which seems to strip out the X-Requested-With field, which makes the ultimate request look non-ajax to the server - hence the redirection.
The reason why it wasn't catching the Illuminate\Contracts\Validation\ValidationException exception is because that exception is not thrown. If you open up Illuminate\Foundation\Validation\ValidatesRequest.php, and search for the function throwValidationException(), it actually throws a HttpResponseException instead. I attempted to catch the HttpResponseException and it was caught successfully - I assume the documentation is wrong.
The solution was to either remove the cross domain attributes on the ajax request, add the X-Requested-With header manually, as per the answer in this post. This would make the application see that it was an AJAX request.
And if you wanted to manually catch the exception, you need to catch the HttpResponseException, not the ValidationException.
I'm developing and College project, It should be a platform that serve and Web and Mobile Front-En. I decided to use Laravel 5.1, my idea was to use laravel as an excelent hardcore php backend to serve this platform, I wanted to develope APIsRest with laravel and comsume the services on both web and mobile front-end. My point is: I don't wanna use blade templating engine, because returning "views, instance objects, etc" my mobile front won't understand responses, that's why I want to orientated my APIs to return JSONs messanges, so I can handle them on each front.
1) First at all I wanna ask you guys if is it a good idea?
2) how can I retrieve the error php messages send by laravel to send them back to views (mobile-web front) as JSONs?
Example: Right now I'm trying to finish the "Login-Register" module of my project. I'm trying to use the advantages of Authentication Laravel Module, and now I am stack on postLogin, because i don't know how to handle the error and send it back to front as JSON.
public function postIngresar(Request $request)
{
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
return response()->json(*$this->sendLockoutResponse($request));
}
$credentials = $this->getCredentials($request);
if (Auth::attempt($credentials, $request->has('remember'))) {
return response()->json([
'success' => 'true',
'message' => 'Logeo logrado exitosamente'
]);
}
if ($throttles) {
$this->incrementLoginAttempts($request);
}
return response()->json($this->getFailedLoginMessage());
}
is there any way to handle all error and exception as JSON messages? It'll will help me A LOT.
thank you so much by reading.
You can read about Exception Handling section of the Laravel docs, but to save you the time, here is the name of the method that will be responsible for returning json representation of your errors:
All exceptions are handled by the App\Exceptions\Handler class. This class contains two methods: report and render. The render method is responsible for converting a given exception into an HTTP response that should be sent back to the browser. By default, the exception is passed to the base class which generates a response for you. However, you are free to check the exception type or return your own custom response
The LoginController uses the AuthenticatesUsers trait which has a method sendFailedLoginResponse which is responsible for the error message redirect upon authentication failure as follows:
protected function sendFailedLoginResponse(Request $request)
{
if ( ! User::where('email', $request->email)->first() ) {
return response()->json([
$this->username() => Lang::get('auth.email')
]);
}
if ( ! User::where('email', $request->email)->where('password', bcrypt($request->password))->first() ) {
return response()->json([
'password' => Lang::get('auth.email')
]);
}
}