Laravel 5.4 - form/API/view - php

I created an API (store) that saves the data on the database and returns 201 if successful or 404 if not.
if ($visit->save()){
$visit->view_visit = [
'href' => 'api/v1/visit/' . $visit->id,
'method' => 'GET'
];
$response = [
'msg' => 'Visit created.',
'visit' => $visit
];
return response()->json($response, 201);
}
$response = [
'msg' => 'Error during creation.'
];
return response()->json($response, 404);
It works perfectly. Using postman you can see that the status will be <<201 Created>>.
This API should be used in two ways: called by another application or called by a Laravel form. This is the question:
How do I call it in a way if it successful, it will load a given view on the browsers?
In other words, is there a way to make the form call a route (the api itself, something like ../api/visit/) and in case of success loads the other view? Also, I would like to pass the content of response['msg'] to this new view.
I know I could do it inside the store method by filtering the HTTP referrer, but I would like to keep the controller code strictly to manage the record creation. Besides that, I have to send the 201/404 codes along with the returned data.
I also considered creating another controller to handle the API response and then call the form, but it still sounds too much -- it's supposed to be easy, I guess.

In laravel you can use a helpful method which determines if the request that has been sent is an AJAX request or just a normal request, which is:
$request->wantsJson()
So, Inside your controller in the return function, you will make an if statement:
if ($request->wantsJson()) {
return response()->json();
}else{
return view(...);
}

Related

Can I push a URL into my application's 'history' when testing in Laravel?

I'm writing a test suite for a Laravel application. I'm making assertions against an endpoint that uses Laravel request validation to validate the user input. It automatically redirects the user back to the previous page.
$request->validate([
'name' => 'required',
'email' => 'required|email',
'timeslot' => 'required'
]);
Currently, I am asserting that I if I post invalid data to this endpoint, I receive a redirect status code back. This is fine, but I would also like to assert, more specifically, that the user is redirected back to the previous page.
I feel that to test this condition properly, I need to somehow 'push' a URL into my test suite's/application's browser history, then assert that the redirect URL is that same URL.
How can I do this?
In a Laravel test, to simulate a previus Url, you use the $this->from($url) method provided by the TestCase class.
An example of it's usage:
$this->from('/home')
->get('/profile')
->assertSee('User profile');
You may even use it with the route() or url() helper:
$this->from(route('home'))
->get('/profile')
->assertSee('User profile');

Different method called when using Laravel form request validation

I have a controller (API\Fields) with a method named store, the route to that method is set up like this:
POST /api/templates/{template}/fields -> API\Fields#store
Everything worked properly until I created a very simple form request validation class with the following rules (This is the only thing I changed besides the return value for the authorize method):
return [
'name' => ['required', 'alpha_num'],
'coordinates' => ['required', 'json'],
'type' => ['required', BaseField::RULE],
'page' => ['required', 'numeric'],
'readonly' => ['sometimes', 'boolean'],
'required' => ['sometimes', 'boolean']
];
After I created the class, I simply changed the request class from Request, to CreateFieldsRequest and it messed pretty much the whole routing for that route up. Instead of calling store, Laravel seems to be calling index. When I restore CreateFieldsRequest back to just the Request class, it behaves as it should again.
I haven't been able to find any information on this topic, and I've verified over and over that I don't have some sort of incorrect routing or redirections on any of the related classes.
Any help or guidance with this would be greatly appreciated, thank you!
When I run the request through the Chrome developer console as a POST request, Laravel kicks it back as a "GET" request, not sure why.
A FormRequest that fails validation issues a redirect. It's the default behavior.
If you issue an AJAX request, or request a JSON response with an Accept header, it'll respond with a JSON list of validation errors and a 422 HTTP code instead.
After running a very simple test I realized that this seems to be an issue with Postman. If you are experiencing this issue stick to adding a _method=POST parameter on your POST body, or simply use XHR or a different API testing tool.
Edit: After further testing I realized the issue had not been fixed. When I run the request through the Chrome developer console as a POST request, Laravel kicks it back as a "GET" request, not sure why.

Laravel REST API request object is empty

Environment
I created an application using Laravel 5.7 and implemented a REST API. I have a route in routes/api.php that triggers a middleware which checks if the incoming request has a parameter called api_token.
This is a production environment and here are the specifics:
Linux Ubuntu 18.04 LTS 64-bit
nginx/1.14.0
Laravel 5.7
PHP 7.2
APP_ENV in the .env file is set to 'production' and APP_DEBUG is set to 'false'.
Problem
My problem is that the incoming request object is always empty when it arrives at the server. At least that's what my Laravel application says.
These are my routes in routes/api.php:
Route::middleware('rest')->group(function() {
Route::get('device-location/{deviceID}', 'PositionDataController#getDeviceLocation');
Route::get('device-location/all', 'PositionDataController#getAllLocations');
Route::post('device-location', 'PositionDataController#storeDeviceLocation');
Route::put('device-location/{deviceID}', 'PositionDataController#updateDeviceLocation');
Route::delete('device-location/{deviceID}', 'PositionDataController#deleteDeviceLocation');
});
The routes are in a middleware group called 'rest' as you can see. I'm using the Route::get('device-location/{deviceID}', 'PositionDataController#getDeviceLocation'); route to test the functionality.
Here's the code from the middleware:
public function handle($request, Closure $next)
{
if(request()->all()) {
$deviceID = request()->device_id;
}
else {
return response()->json([
'error' => 'The request object is empty.',
'request' => request(),
'parameters' => request()->all(),
'content' => request()->getContent(),
'input' => request()->input()
], 500);
}
$device = MobileDevice::where('device_id', $deviceID)->first();
if($device) {
$deviceToken = $device->api_token;
if($deviceToken == request()->api_token) {
return $next($request);
}
else {
return response()->json(['error' => 'Token does not match.'], 500);
}
}
else {
return response()->json(['error' => 'The device with the id [' . $deviceID . '] could not be found.'], 500);
}
}
The middleware first checks if there are parameters in the request object and then does some logic to check if the right token was sent. If the request object is empty it returns some data to help me understand what went wrong.
I use Postman (https://www.getpostman.com) to test the API. Here's my Postman setup:
Postman setup
Postman headers
This is the response I get in Postman:
Postman response
I get the same result if I call that route in a browser.
Regardless of if I put in parameters or not the request seems to be always empty.
Here are the things that I've tried to do:
Not using the middleware
Using the $request variable instead of the helper request()
Switching between 'application/json' and 'application/x-www-form-urlencoded' in the Headers of my Postman setup
Calling that route in a browser
Updating to Laravel 5.7
The strange thing is that all of this works perfectly on my local environment and on a test server that has the same specs as the production server.
UPDATE 01:
So it seems to be even worse...
If I add a route like this in web.php:
Route::get('/request-return', function() {
return request()->all();
});
and visit that route like this:
laravel.application/request-return?device_id=1&api_token=XgkQLs8G7OYTqdwsXmjJT5G9bxv20DRi
I get back an empty array [].
So it seems like the parameters don't get to the server itself.
You are getting device id through GET request so use the below line instead of $request()->device_id.
Use this and let me know
$name = $request->input('device_id')
Okay I could solve the problem. It didn't have anything to do with Laravel. It was a nginx configuration problem:
https://serverfault.com/questions/231578/nginx-php-fpm-where-are-my-get-params

Laravel api route validation not validating

I did move from Lumen to Laravel and now converting my project over. Everything is working except the validation. For some reason, if I try to validate, it just redirects to the welcome.blade.php view. What could cause this?
I am using only the API part of routes, not the view. I am not dealing with views. I am using the stateless part of Laravel.
According to documentation, I can validate like this:
$this->validate($request, [
'title' => 'required|unique|max:255',
'body' => 'required',
]);
If validation passes, your code will keep executing normally. However,
if validation fails, an
Illuminate\Contracts\Validation\ValidationException will be thrown.
I also tried to force it to return JSON response without success:
$validator = $this->validate($request, ['email' => 'required']);
if ($validator->fails()) {
$messages = $validator->errors();
return new JsonResponse(['status' => 'error', 'messages' => $messages]);
}
However, mine doesn't fail but just returns the welcome view with response code of 200. I have tried pretty much all the possible validation methods from the documentation and from google. Non of them are working.
I even tried with clean laravel install, declared one test route and test controller which had the validation and the result is the exact same.
Is the validation even meant to be compatible with the restful/stateless part of Laravel?
Any suggestion is much appreciated.
1- first the unique key needs a table, per example if you want the email to be unique in the users table you do as follows:
'email' => 'required|unique:users',
I think may be you have placed your route in route/web.php file. Replace that code from web.php to api.php
Try to place your API endpoints in route/api.php file.
And remember you need to add prefix /api in your route.
Ex : test.com/api/users.

Laravel 5.2 PHPUnit JSON Api request body not being set

I am testing POSTing data to an API endpoint we've created using Laravel 5.2, but none of the parameters seem to be reaching the application in the test. The endpoint expects json and responds with json and uses a FormRequestValidator which has required rules for active and make parameters. The test fails with status code 422 and the examining the response body it states the active and make parameters are required even though they are being passed in the call so therefore for some reason when the request reaches the the Form Request Validator, the input is not there.
However, when I invoke the endpoint with json body including make and active from Postman or the UI app we've built it works fine, it is only failing in the PHPUnit tests therefore it must be something with PHPUnit or the test setup being incorrect. Here is the test:
public function testItStoresCars()
{
// Arrange
$user = User::first();
//Act
$this->json(Request::METHOD_POST, '/v1/cars', [
'active' => true,
'make' => 'Audi'
],
['Authorization' => 'Bearer '.\JWT::fromUser($user)]));
// Assert
$this->assertResponseOk();
}
I know the Authorisation header is set correctly from other tests passing.
I've tried disabling middleware, using the $this->post helper method and manually setting the headers as well as using the $this->call method with setting the Headers and encoding the data using json_encode but I always get the same 422 response. I'm wondering has anyone encountered this issue or can see an error?
Controller Code
public function store(CreateCarRequest $request)
{
$car = $this->carRepo->save($request->all());
return response()->json(car);
}
FormRequest
class CreateCarRequest extends Request
{
public function rules()
{
return [
'active' => 'required|boolean',
'make' => 'required',
];
}
}
422 is the error response for validation errors.. which means either your data is not posted or it doesn't pass server validation, try
$this->json(Request::METHOD_POST, '/v1/cars', [
'active' => true,
'make' => 'Audi'
],
['Authorization' => 'Bearer '.\JWT::fromUser($user)]))->dump();
dump() should show you the errors

Categories