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.
Related
I am using Laravel Spark for billing my customers. In case they will pay with 3D Secure cards (I test it with card no. from stripe doc's https://stripe.com/docs/testing#regulatory-cards: 4000 0027 6000 3184) I have the following problem:
The stripe pop-up/modal opens and I click 'Complete Authentication'.
After that the authentication process starts and the user gets directed to http://127.0.0.1:8000/stripe/payment/pi_xxx?redirect=/home
Here I get the following Laravel error (I do not find other error causes in console or somewhere else):
I've added stripe/* in VerifyCsrfToken Class already...
Maybe do I need to test this case on my server?
Very weird and I guess thats a sign that I do not have any issues with stripe, with laravel instead. When I remove the query parameter ?redirect=home I get this screen:
When I proceed I do not get redirected.. Of course because there is no redirect uri...
Does any one had this issue before?
For me this is a bug in Spark. I've searched for all occurences where Stripe used a redirect. One indication for me that this could really be a bug is:
In subscription-notice.blade.php file there ist the link build as follows:
{!! __('Please :linkOpen confirm your payment :linkClose to activate your subscription!', ['linkOpen' => '', 'linkClose' => '']) !!}
The part '?redirect='.url('/home').' creates a full valid URL with host address.
Not only a relative path! These relative paths runs into the 403 Error in my case.
Like in the RegisterController:
/**
* Handle a registration request for the application.
*
* #param \Laravel\Spark\Contracts\Http\Requests\Auth\RegisterRequest $request
* #return \Illuminate\Http\Response
*/
public function register(RegisterRequest $request)
{
list($user, $paymentId) = Spark::interact(
Register::class, [$request]
);
Auth::login($user);
event(new UserRegistered($user));
if ($user instanceof MustVerifyEmail && ! $user->hasVerifiedEmail()) {
$user->sendEmailVerificationNotification();
}
return response()->json([
'redirect' => $paymentId ?
'/'.config('cashier.path').'/payment/'.$paymentId.'?redirect='.$this->redirectPath()
: $this->redirectPath(),
]);
}
$this->redirectPath() returns a relative path. I've changed this part into:
return response()->json([
'redirect' => $paymentId ?
'/'.config('cashier.path').'/payment/'.$paymentId.'?redirect='.config('app.url').$this->redirectPath()
: $this->redirectPath(),
]);
In this case I took the host address from my config and put it in front of the relative path.
Just for better understanding, the returned URL above is used here (register-stripe.js):
/*
* After obtaining the Stripe token, send the registration to Spark.
*/
sendRegistration(paymentMethod) {
this.registerForm.stripe_payment_method = paymentMethod;
Spark.post('/register', this.registerForm)
.then(response => {
window.location = response.redirect;
});
}
There are some more cases where I needed to override some JavaScript or PHP sources...
register process (showed here)
update process of payment information
create subscription with existing account
I hope I could help others with that! If necessary I could also post the exact places where I've changed the redirect URL in the comments.
I came up with a (dirty?) workaround:
In my StripWebHookController there is some code that makes a notification for the user:
if ($billable) {
$model = config('cashier.model');
$notifiable = $billable instanceof $model ? $billable : $billable->owner;
if (in_array(Notifiable::class, class_uses_recursive($notifiable))) {
$payment = new Payment(StripePaymentIntent::retrieve(
$payload['data']['object']['payment_intent'],
$billable->stripeOptions()
));
$notifiable->notify(new $notification($payment));
}
}
Now, this is a notification that apparently makes the StripePaymentIntent notification that is created in the PaymentController of Cashier (located /var/www/html/site/vendor/laravel/cashier/src/Http/Controllers)
There is a VerifyRedirectUrl middleware that is causing the problem right. So when you comment it out, the 403 disappears:
public function __construct()
{
//$this->middleware(VerifyRedirectUrl::class);
}
However, the "go back" button after accepting the payment is not working so I'll check that out. But for now, this 403 is at least gone. If I can't another solution, I'll go with this.
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.
Hi I'm trying to create a test for a laravel controller not dependent on the view. I have the following method in my controller class:
public function store(Request $request)
{
$this->validate($request,[
'name'=>'required',
'login'=>'required',
'password'=>'required'
]);
//TODO: Store to database
return redirect('usuario/nuevo');
}
And I found the following code to test whether the request had any errors:
public function testStore()
{
$response=$this->call('GET','usuario.store');
$this->assertSessionHasErrors();
}
As it is this test should pass since I'm sending a request without the required field filled out however PhpUnit returns the following message:
Session missing key: errors
Failed asserting that false is true.
The only way I can make it work is to try to "see" the error message on the response page by making the test like:
public function testStore()
{
$this->visit('/usuario/nuevo')
->press('Crear')
->see('Whoops');
}
This doesn't work for me for two reasons: 1)The test depends on the view to "press" the button and send the request(I wish to test this in a different test and keep this one strictly for the controller) and 2)The test depends on the 'Whoops' string to be present which is obviously bad practice.
I have found several answers claiming that the fist test code should work, but it simply doesn't.
Try it with session start and send empty parameters
$this->startSession();
$this->call('GET','usuario.store', array(
'name' => '',
'login' => '',
'password' => ''
));
$errors = session('errors');
$this->assertSessionHasErrors();
$this->assertEquals($errors->get('name')[0],""your custom error message);// only for checking exact message
Your problem is you are not testing what you think you are testing.
To execute the controller's store() you should use a POST method and not GET unless your routes are really weird.
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')
]);
}
}