I'm using https://github.com/sahat/satellizer to login.
It sends requests with header Content-Type:application/json (body with json ex:{login:login, password:pass})
On server side I use Slim Framework.
In order to retrive sent values I'm getting it through BODY
$app = new \Slim\Slim();
$app->add(new \Slim\Middleware\ContentTypes());
$app->post('/auth/login', function () use ($app) {
$params = $app->request()->getBody();
print_r($params);//I've got it
});
Can I retrive those params through post?
if ($app->request()->isPost()) {
$email = $app->request->post('email');
$password = $app->request->post('password');
}
$_POST is meant for form data. As you noticed you can can find JSON in request body instead.
$app->post('/auth/login', function () use ($app) {
$params = $app->request->getBody();
print_r(json_decode($params, true));
});
I don't know if this is built in to Slim but I use my own middleware to make it work.
class MyMiddleware extends \Slim\Middleware {
function call() {
$body = $this->app->request->getBody();
if (is_array($body))
$this->app->environment()['slim.request.form_hash'] = $body;
$this->next->call();
}
}
This should be added before the ContentTypes middleware to ensure the correct ordering.
$app->add(new MyMiddleware());
$app->add(new \Slim\Middleware\ContentTypes());
Related
I am wrote API method, after calling that method , I got my response like
[
{
"spark_version": "7.6.x-scala2.12"
}
]
Now I want to have variable in my API method which store value 7.6.x-scala2.12.
API Controller method
[HttpGet]
public IActionResult GetTest(int ActivityId)
{
string StoredJson = "exec sp_GetJobJSONTest " +
"#ActivityId = " + ActivityId ;
var result = _context.Test.FromSqlRaw(StoredJson);
return Ok(result);
}
So how this variable should call on this response to get string stored in spark_version?
Thank you
As you have the JavaScript tag, here's how you'd do it in JS:
If you are able to call your API method, then you can just assign the response to a variable. For example, if you are calling it using the fetch API, it would look something like:
let apiResponse;
fetch('myApiUrl.com').then((response) => {
if (response.status === 200) {
apiResponse = response.body;
console.log('Response:', apiResponse[0]['spark_version']);
}
});
(I defined the variable outside the then to make it globally accessible)
So I want to allow users to either request .xml or .json responses when they run a rest API request to the server. (much like twitter)
But I don't belive that the following way is the best way, as it means duplicate code, surely there is a better way to allow .xml or .json response.
$app->get('/books/:id.xml', function ($id) use ($app) {
$app->render('/xml/books.xml', array('id' => $id));
});
$app->get('/books/:id.json', function ($id) use ($app) {
$app->render('json/books.json', array('id' => $id));
});
OR
// Define app routes
$app->get('/hello/{name}.{type}', function ($request, $response, $args) {
//return $response->write("Hello " . $args['name']);
if($args['type'] == 'xml')
{
return 'this is xml';
}
var_dump(parse_url($_SERVER['REQUEST_URI']));
});
if anyone knows how to do this, that would be great.
Consider using value of Accept HTTP header of request instead of file extension in the end of the URI.
I'd say using header is more reliable and more "proper" way to determine the format in which the data should be returned.
URI should be used to point to specific resource. Accept header should be sent by client to tell you what format the data should be returned.
Apart from being standard way to implement a RESTful service, it removes the headache of altering routes (like in your second example).
If you agree to such implementation, there's an excellent library to solve your problem.
It has lots of uses, and here is an example for your use case:
<?php
use Negotiation\Negotiator;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
class YourController
{
public function __construct(Negotiator $negotiator, DataProvider $someDataProvider)
{
$this->negotiator = $negotiator;
$this->someDataProvider = $someDataProvider;
}
/**
* Processing request.
*
* We get data, then we use Negotiator to detect what format the requestor prefers.
* Then we return data in requested format or in case format is not supported,
* fall back to JSON.
*
*
* #param Request $request
* #param Response $response
* #return Response
*/
public function __invoke(Request $request, Response $response)
{
$data = $this->someDataProvider->getSomeData();
$mediaType = $this->determineMediaType($request);
switch ($mediaType) {
case 'application/json':
default:
// $data = $data->asJson();
// transform data to JSON...
break;
case 'application/xml':
$data = $data->asXml();
// transform data to XML...
break;
}
// Write data to body of response
$response->getBody()->write($data);
// Set appropriate response header
return $response->withHeader('Content-Type', $mediaType);
}
/**
* Find preferred data format from Accept header.
*
* Uses Negotiator to determine whether JSON or XML should be returned.
*
* #param Request $request
* #return string
*/
private function determineMediaType(Request $request)
{
$acceptHeader = $this->extractAcceptHeader($request);
// Set list of "known" formats, i.e. formats that your service supports
$known = ['application/json', 'application/xml'];
// Let negotiator determine what format should be used
$mediaType = $this->negotiator->getBest($acceptHeader, $known);
if ($mediaType) {
return $mediaType->getValue();
} else {
return 'application/json'; # if request has unexpected value of accept header, default to JSON
}
}
/**
* Extract Accept header value from Request object
* #param Request $request
* #return string
*/
private function extractAcceptHeader(Request $request)
{
return $request->getHeaderLine('Accept');
}
}
This class is an example of a callback to a route. Such implementation allows you to easily extend the list of supported formats without tampering routes.
What's the best way to obtain this response to invalid form?
Response example of an invalid form
Actually I have this action
public function postUserAction(Request $request)
{
...
$form->handleRequest($request);
if ($form->isValid()) {
...
return $this->handleView($view);
}
$errors = $form->getErrors(true);
$view = $this->view($errors);
return $this->handleView($view);
}
But the response is the next json object:
{ form: Object, errors: Array }
I work with JMSSerializerBundle. I saw in FormErrorNormalizer class the method normalize in FOSRestBundle.
Thanks,
Request's handleRequest is for html forms. You have to use submit instead for FOSREST.
$form->submit($Request->getContent());
I have a language parameter that needs to be sent to my documents endpoint. So I have to validate that user has sent this parameter in his GET request.
Making rule in my model didn't do anything:
public function rules()
{
return [
[['language'], 'required'],
];
}
Because of that I have tried this:
1) I have created ParamsValidator class:
<?php
namespace app\modules\v1\components;
use yii\web\UnprocessableEntityHttpException;
use yii\base\Component;
use Yii;
/**
* Class that is responsible for validating input params.
*/
class ParamsValidator extends Component
{
public function validate($params)
{
if (!isset($params['language'])) {
throw new UnprocessableEntityHttpException("Language parameter is required");
}
}
}
I am invoking its validate() method inside my controllers init() method:
public function init()
{
$this->_params = Yii::$app->request->queryParams;
$validator = new ParamsValidator();
$validator->validate($this->_params);
}
And this sort of work. Code works, but I get ugly response back. Instead of nice JSON response, I get bunch of html starting like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Unprocessable entity (#422)</title>
<style>
body {
font: normal 9pt "Verdana";
color: #000;
background: #fff;
}
Instead of this html, I would like some nice JSON response like this:
{
"name": "Forbidden",
"message": "You are not authorized to do this.",
"code": 0,
"status": 403,
"type": "yii\\web\\ForbiddenHttpException"
}
This nice JSON error that you see is made by:
$behaviors['authenticator'] = [
'class' => HttpBasicAuth::className(),
'auth' => [$this, 'authenticate']
];
But obviously my Validator is not doing this.
Questions:
1) How to validate params that comes via GET request ?
2) If my method is right, how to get this nice JSON error response ?
A simple option is by overriding the ActiveController::checkAccess method by adding this inside controller:
public function checkAccess($action, $model = null, $params = [])
{
if ($action === 'index' or $action === 'view')
{
$params = Yii::$app->request->queryParams;
if (!isset($params['language'])) {
throw new \yii\web\ForbiddenHttpException('You are not authorized to do this.');
}
}
}
In case you need to do it at model level you'll need to use the addError() method instead of directly throwing the error. Your model instance will hold it when invoking its validate method. Then inside your action you can simply return it. it will be serialized and errors will be outputted in the right format. There is many ways to achieve this. A simple example may be by using the load method to pass both query and body params to the model before validating it (scenarios may be required):
$queryParams = Yii::$app->request->queryParams;
$bodyParams = Yii::$app->request->bodyParams;
$params = array_merge($queryParams,$bodyParams );
$model = new $modelClass;
$model->load($params , '');
if ($model->validate() === false) return $model;
else {
// do whatever you need
}
I'm looking for a way to access the JSON being sent back to the requestor in the "after" filter for a controller.
var locomotive = require('locomotive');
var myController = new locomotive.Controller();
myController.after('myAction', function(next) {
var response = {}; //I want to access the JSON being sent back in myAction: {'hello':'world'}
console.log(response); //this should log "{'hello':'world'}"
next();
});
myController.myAction = function myAction() {
this.res.json({'hello':'world'});
}
module.exports = myController;
If anyone has any way of doing this, it would be much appreciated.
In your main action, assign your json to an object on this (res is reserved):
myController.myAction = function myAction() {
this.model = {'hello':'world'};
this.res.json(this.model);
}
Then you can access it in your after filter:
myController.after('myAction', function(next) {
var model = this.model;
console.log(model);
next();
});
I found a "hack" solution... It's not the cleanest, and requires changing the code within the express response.js file in "node_modules"...
If anyone has a better option where you can access the json being sent in response to the request within the controller action (or controller filter) itself, I'd greatly appreciate it.
Thanks.
in the ~/node_modules/locomotive/node_modules/express/lib/response.js file, I altered the "res.json" function (line 174 for me) to include the following line after the declaration of the body variable (which is passed to the send function).
this.responseJSON = body;
This allows you to access this.responseJSON within a controller's after filter, as follows:
myController.after('myAction', function(next) {
**var response = this.res.responseJSON; //ACCESS RESPONSE JSON HERE!!!!!**
console.log(response); //Now logs "{'hello':'world'}"
next();
});
Like I said, not the most elegant, but gets the job done in a pinch. Any more elegant solutions welcome...