Force JSON Response in CakePHP - json

I've created routes shown below:
Router::connect('/:api/:controller/:action/*', array(), array('api'=>'api'));
Router::connect('/:api/:controller', array('action' => 'index'), array('api'=>'api'));
Router::connect('/:api/', array('controller' => 'index', 'action' => 'index'), array('api'=>'api'));
Basically, I want all requests made through a particular endpoint to respond in JSON. In the case above all requests made with the api prefix. For example:
http://localhost/api/products
Should return a JSON response instead of an HTML. Note that it should work that way even without the .json extension being defined.

So I am guessing in your controller you check if the api prefix was set and if so you serialize the data you give back to the view? if so then just add:
$this->RequestHandler->renderAs($this, 'json');

Related

How to force restful responses in json?

I want to add some api to my Yii2 site. Api must be only in json. I don't want to set Accept: application/json headers for each request. I can set 'response' => ['format' => \yii\web\Response::FORMAT_JSON] in application configuration but it breaks all pages. Also my api function returns data in xml.
I tried to use rest\ActiveRecord for my purposes. Maybe I do it's wrong. What I want.
To have my Yii2 based site with some api acсessed through https://example.com/api/controller/action. In project I want to see folder controllers/api which contains my controllers. Controllers must use standard \yii\db\ActiveRecord based models. Also controllers input paramaters only in json body or as part url and output data only in json.
You may need to set the following code in the controller's action somewhere before return or in beforeAction() method:
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
also since Yii 2.0.11 there is a dedicated asJson() method to return a response in JSON format:
return $this->asJson($array);
The more elegant solution is to use yii\filters\ContentNegotiator.
When the Accept header is missing ContentNegotiator assumes it allows any type and send response in first format defined in its $formats property. If the requested format is not among accepted formats the content negotiator will throw yii\web\NotAcceptableHttpException and app will respond with http status 406 Not Acceptable.
You can add it in your controller in behaviors() method like this:
public function behaviors()
{
return [
[
'class' => 'yii\filters\ContentNegotiator',
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
],
];
}
If your controller extends yii\rest\Controller it already has the ContentNegotiator filter added among its behaviors. You only need to limit allowed formats like this:
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['contentNegotiator']['formats'] = [
'application/json' => \yii\web\Response::FORMAT_JSON,
];
return $behaviors;
}
Using ContentNegotiator instead of explicitly forcing the JSON format in beforeAction() will allow for easier addition of other formats if they are needed in future.

Laravel 5.8 validation return to home after it fails while using Passport

I have a store() method.
public function create(StorePost $request)
{
$post = Post::create([
'title' => $request->title,
'description' => $request->description,
]);
return response()->json([
'post' => $post
], 201);
}
In my StorePost class, I validated the request.
public function rules()
{
return [
'title' => 'required|string|max:255',
'description' => 'required',
];
}
When I tested it with Postman with wrong entries, for example, if I fill 'description' with a null value, it returns to the home page without any response or error. How can I retrieve the validation errors?
I solved it just by adding 'Accept': 'application/json' in my request's header.
This happens because Laravel Form Request works both for API and non-API requests.
On requests made by a form, it will redirect back (to the original form url or home if not sent from a form) with an error bag on $errors variable available to the view.
On requests made by ajax (usually on APIs) usually have the header Accept: application/json, so Laravel automatically knows that you want the validation error bag as json on the response body instead of a redirect that makes no sense for an API.
Hope this can clarify thing to you.

Is it possible to set the path for all frontend AJAX request

My slugs looks like this:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'<lang>/<p_page>/<p_category>/<p_product>' => 'splitter/manage-request',
'<lang>/<p_page>/<p_category>' => 'splitter/manage-request',
'<lang>/<p_page>' => 'splitter/manage-request',
'<lang>/' => 'splitter/manage-request',
'<lang>' => 'splitter/manage-request',
'' => 'splitter/manage-request',
],
],
I am sending all the requests to that SplitterController where I am parsing them. But I don't want the AJAXs to be send to it. Is it possible to give them some default route? To redirect them backend for example. I couldn't find information about it in the documentation? Appreciating links also is I missed it. Thank you!
According to my experience, there are probably the following options for your reference:
the first: The easiest way is to add a beforeAction() to your SplitterController, determine if it is an ajax request in beforeAction(), and redirect to your target address, for example:
public function beforeAction()
{
if (\Yii::$app->request->isAjax) {
return \Yii::$app->response->redirect($targetUrl);
}
return parent::beforeAction($action);
}
second: You can check if the request is an ajax request in the nginx configuration and then redirect to your target address
third: If you can't set up additional routing rules and configure the ajax request address, then you can consider implementing it by customizing UrlManager::parseRequest(), just check if it is an ajax request before it starts parsing the request, then redirect to your target. address
This answer is translated from Google Translate

Json::decode returns NULL

I have problem with Json::decode. I'm using this code:
use Drupal\Component\Serialization\Json;
$client = \Drupal::httpClient();
$request = $client->post($rest_url, [
'form_params' => [
'id' => $rest_id,
],
]);
$response = Json::decode($request->getBody());
to get JSON from some server but it returns NULL. Of course this is just a part of the code (without try, catch...)
$request->getBody() return is ok, but in Json::decode I'm still getting NULL.
The only thing I noticed is that in Postman, when I look at raw body content, I see some empty lines at the beginning of the JSON (like return on keyboard when typing), but I checked JSON as it is on JSONLint and it's valid.
Any idea what is the problem?
I'm not familiar with Drupal's JSON serializer, but try to force response body conversation to a string.
$response = Json::decode($request->getBody()->getContents());
Guzzle return a Stream object from getBody(), it could be the issue.

Return JSON based on "Accept: application/json" from a Symfony2 controller

I've got a CRUD form generated via the SensioGeneratorBundle, as described here. This works great.
However, I would like to also return JSON, rather than HTML responses, if the "Accept" HTTP header contains only "application/json". I'm working on a prototype for a JSON service and this would help me jump start things.
I figured out that I can turn my entities into a JSON string like this:
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$json = $serializer->serialize($entity, 'json');
However, at the end of that, $json contains a string that is my JSON data. I want to just directly output that to the requestor, rather than render the usual view. I've tried returning a new JsonResponse($json), but it re-encodes the JSON string, so it winds up double-encoded.
So I have two questions:
What is the "correct" way to inspect the HTTP requestion headers? I know I can just look in $_SERVER, but I'm thinking that there may be a better way to do this in Symfony2.
What is the "correct" way to return a JSON string, or to translate my entities into JSON that is returned straight to the requestor, without rendering the usual view.
Thanks!
This will verify if the current request is XHR and then send back properly formatted JSON data:
public function someAction(Request $request)
{
if ($request->isXmlHttpRequest()) {
$serializer = new Serializer(array(
new GetSetMethodNormalizer()
), array(
'json' => new JsonEncoder()
));
$response = $serializer->serialize(array(
'success' => true,
'data' => array(
'entity' => $entities,
)
), 'json');
return new Response($response, 200, array('Content-Type' => 'application/json'));
} else {
// Run "normal" request code, render a view
}
}
By the way, JMSSerializerBundle makes the serializing syntax more straightforward (because, let's face it, the native Symfony way for this is ugly) and also provides some additionnal features such as excluding entity fields to serialize (through annotations).
With JMS, my code looks like this:
if ($request->isXmlHttpRequest()) {
$response = array('success' => true, 'data' => array(
'entity' => $this->container->get('serializer')->serialize($entity, 'json'),
'lastPage' => $lastPage,
));
return new Response(json_encode($response), 200, array('Content-Type' => 'application/json'));
}
And finally, 'success' and 'data' are in no way required, it's just the structure I use to split status and data to be readable in JavaScript.