I'm trying to return some JSON data from my controller. I actually got it working with.
response()->json(['success'=>true])->send();
but that is not the way it's done in the documentation. I tried
return \Response::json(['success' => true]);
and the status code was 200 but no data in the body. I guess it's okay but I just really want to know what the problem is. There were nothing in the log so there does not seem to be an error. If it's any help, I'm using Laravel 5.5.40 and a dependency called tymon/jwt-auth which applies some middleware for auth and refresh.
Should probably add that I have tried to simply return an array and string but the result stays the same.
CONTROLLER
Notice the commented section in createGame
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Game;
use Illuminate\Http\Response;
class GameController extends Controller
{
public function getPostPatchDelete(Request $req){
switch($req->method()){
case 'GET':
$this->getGame($req);
case 'POST':
$this->createGame($req);
case 'PATCH':
$this->updateGame($req);
case 'DELETE':
$this->deleteGame($req);
}
}
private function getGame($req){
}
private function createGame($req){
//Response::json(['success' => 'hi, atiq']);
//return response()->json(['lel'=>'lol'], 200);
response()->json(['success'=>true])->send();
}
private function updateGame($req){
}
private function deleteGame($req){
}
}
You have to use return in getPostPatchDelete() AND createGame():
public function getPostPatchDelete(Request $req){
switch($req->method()){
case 'POST':
return $this->createGame($req);
}
}
private function createGame($req){
return response()->json(['success'=>true]);
}
The way I always do it is like this:
return response()->json(['message' => 'Some message.'], 200);
Note: the second argument of the json() function is the status code.
Of course is always nice to use descriptive constants instead of numbers -in this case to set the HTTP response code- so:
use Illuminate\Http\Response;
class CoolController extends Controller {
public function coolFunction() {
// your logic
return response()->json(['message' => 'A message.'], Response::HTTP_OK);
}
}
Related
I am creating a new API call for our project.
We have a table with different locales. Ex:
ID Code
1 fr_CA
2 en_CA
However, when we are calling the API to create Invoices, we do not want to send the id but the code.
Here's a sample of the object we are sending:
{
"locale_code": "fr_CA",
"billing_first_name": "David",
"billing_last_name": "Etc"
}
In our controller, we are modifying the locale_code to locale_id using a function with an extension of FormRequest:
// This function is our method in the controller
public function createInvoice(InvoiceCreateRequest $request)
{
$validated = $request->convertLocaleCodeToLocaleId()->validated();
}
// this function is part of ApiRequest which extend FormRequest
// InvoiceCreateRequest extend ApiRequest
// So it goes FormRequest -> ApiRequest -> InvoiceCreateRequest
public function convertLocaleCodeToLocaleId()
{
if(!$this->has('locale_code'))
return $this;
$localeCode = $this->input('locale_code');
if(empty($localeCode))
return $this['locale_id'] = NULL;
$locale = Locale::where(Locale::REFERENCE_COLUMN, $localeCode)->firstOrFail();
$this['locale_id'] = $locale['locale_id'];
return $this;
}
If we do a dump of $this->input('locale_id') inside the function, it return the proper ID (1). However, when it goes through validated();, it doesn't return locale_id even if it's part of the rules:
public function rules()
{
return [
'locale_id' => 'sometimes'
];
}
I also tried the function merge, add, set, etc and nothing work.
Any ideas?
The FormRequest will run before it ever gets to the controller. So trying to do this in the controller is not going to work.
The way you can do this is to use the prepareForValidation() method in the FormRequest class.
// InvoiceCreateRequest
protected function prepareForValidation()
{
// logic here
$this->merge([
'locale_id' => $localeId,
]);
}
I have extended a class with my own, and if I hit method respondNotFound (which I am certainly hitting because i.e. dd() works if I write it in ApiController) it won't return anything. It just somehow skip return from respondNotFound and goes to this return below if statement with all null values for all data that are provided with json(what is actually a normal case, but this line should never be executed).
class LessonsController extends ApiController{
...some other methods here...
public function show($id)
{
$lesson = Lesson::find($id);
if (! $lesson) {
$this->respondNotFound('Lesson does not exist');
}
return Response::json([
'data' => $this->lessonTransformer->transform($lesson),
], 200);
}
and the ApiController looks like:
use Illuminate\Support\Facades\Response;
class ApiController extends Controller{
protected $statusCode = 404;
public function getStatusCode()
{
return $this->statusCode;
}
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
}
public function respondNotFound($message = 'Not Found')
{
return Response::json([
'error' => [
'message' => $message,
'status_code' => $this->getStatusCode()
]
]);
}}
I have also tried to call getter for statusCode in LessonController but same thing, it just does not reacting to return. But, as I mentioned above, If I die and dump anything in ApiController everything is OK, it works as it should, so if I dd this Response::json... I get it on the front.
Anyone had this issue yet, or any ideas what could be the cause?
I think you problem lies in the logic block. Try the following:
if (!$lesson){
return $this->respondNotFound('Lesson does not exist');
}
You must stop the script with return keyword in the if block, else the script in the LessonsController will continue to run even after the respondNotFound() call.
You probably just need to add return before your $this->respondNotFound('Lesson does not exist'); and all is going to work
I am trying to do a small rest api which based on Lumen. Everything is ok for this sample:
routes/web.php
$router->post('foo/', function () {
return response('[]', 400)
->header('Content-Type', 'application/json');
});
postman receives this response:
400 - bad request. That's ok. But if I try to do the same with a controller php file:
routes/web.php
$router->post('accounts/', 'AccountController#register');
app/Http/Controllers/AccountController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class AccountController extends Controller {
public function __construct() {}
public function register(Request $request): string {
return response()->json(['message' => 'failed'], 400);
}
}
status code is: 200? But why?
Headers came in body response...
Can you advice something to solve this case?
Defining function's return type like string you tell php to make everything (unless strict_types declared) to convert anything to string. I presume somewhere in Response class there's a __toString method which outputs data as you see in your picture. So, just remove return type declaration. Or change it to Response. It is Response class's responsibility to process data and output it as required, not your controller:
public function register(Request $request)
{
return response()->json(['message' => 'failed'], 400);
}
My question is simple :
Is it possible to set the http response code before the controller method return (if I return a php table, for json response).
I know that it's possible to do that :
return response()->json($json,HTTP_CODE);
But I want something to set the code somewhere in the controller without modifying the final return.
Or do you know a way to make the native php function http_response_code working ?
Because Laravel overwrite it during building the Response.
Is it possible ? Or you have to do it in the return ?
I want to know if it is possible to do that or not :
public function myMethod(){
//Some code
$this->injectHttpCode(400); //or how to use native native http_response_code(400); ?
//Some code
return $this->json; //I dont want to modify that
}
I dont want any "return" in you answer, just tell me if it's not possible.
If you want to have some sort of standard way to handle the response code, the easiest way to do is to have a base BaseController that contains this:
protected function getResponseStatusCode() : int
{
switch (request()->getMethod()) {
case 'GET':
case 'PUT':
return 200;
case 'POST':
return 201;
case 'DELETE':
return 204;
default:
return request()->getMethod();
}
}
And:
public function respond($json)
{
return response()->json($json, $this->getResponseStatusCode());
}
so in your controller you only call the method in the base controller with:
return $this->respond($json);
How about using response() function to do something like this:
public function myMethod(){
$response = response();
//Some code
$response->setStatusCode(400);
return $response->json([]);
}
I want to set up the proper routes and models so when I visit www.myapp.com/search?keyword=test I get a view with data and when I visit api.myapp.com/search?keyword=test I get JSON representation of the exact same search result.
If I sort out the subdomain routes like so:
Route::group(array('domain' => '{subdomain}.myapp.com'), function()
{
Route::get('/', function($subdomain)
{
dd($subdomain);
});
});
What's the simplest way to use the same model method but instead of returning a view (for non-subdomain routes) with the array of model objects like so:
return view('search', compact('models'));
I'd return an array of JSON objects, simply like so:
return $models;
Note: a subdomain is not that necessary, it can also be myapp.com/api/search?keyword=test
Thanks.
There are several way to do this.
One option is to not use a separate subdomain or path at all. Instead, use the built-in mechanism to check which Content-Types are in the Accept header of the request.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function getSearch(Request $req)
{
$keyword = $req->input('keyword');
$results = yourCodeThatDoesTheSearch();
// If the request has header `Accept: */json`, return JSON
if ($req->wantsJson())
{
return $results;
}
// Otherwise, return view
return view('search', compact('results'));
}
}
However, you may still wish to do it with a subdomain, either because you can't control the request headers, or because you want to have an easy indicator of what response you will get.
Here's the easiest way to do it with a subdomain:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function getSearch(Request $req)
{
$keyword = $req->input('keyword');
$results = yourCodeThatDoesTheSearch();
// If the request came to the API subdomain, return JSON
if ($req->route('subdomain') === 'api')
{
return $results;
}
// Otherwise, return view
return view('search', compact('results'));
}
}
However, if you plan to do this in many places in your code, you should avoid repeating the same if statement everywhere.
Instead, create yourself a helper function:
<?php
use Illuminate\Contracts\Routing\ResponseFactory;
if (! function_exists('response_auto')) {
function response_auto($view, $data, $request, $status = 200, array $headers = [])
{
$factory = app(ResponseFactory::class);
if ($request->wantsJson())
{
return $factory->json($data, $status, $headers);
}
return $factory->view($view, compact('data'), $status, $headers);
}
}
You can then use it in your controller like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function getSearch(Request $req)
{
$keyword = $req->input('keyword');
$results = yourCodeThatDoesTheSearch();
return response_auto('search', $results, $req);
}
}
Adding to Moshe Katz answer. If you just perform a $req->wantsJson() in your controller, works fine. However, there are occasions where if the user hits the back button, json will be returned in the view due to caching. A robust api would be in it's own controllers and use a repository for your db queries. That may be overkill.
Another option is to use a query parameter in your url.
Example:
in your ajax client (axios)
axios.get('/stuff', {params: {json: true}).then(response => return response);
this will send a request to
/stuff?json=true
in your laravel controller (StuffController#index)
public function index(Request $request)
{
$stuff = Stuff::all();
if ($request->query('json')) {
return $stuff;
}
return view('stuff.index', compact('stuff'));
}
This allows you to easily identify if the request wants json, prevents unwanted returns due to browser caching and keep the code in the same controller.