I am trying to add user-specific Metadata to every API response using Dingo Api. I assumede this would be done in an AddMetadata middleware:
<?php
namespace App\Http\Middleware\Api;
use Closure;
use Dingo\Api\Http\Request;
class AddMetadata {
public function handle(Request $request, Closure $next)
{
$response = $next($request);
/*
* Dingo API response has the ability to modify metadata responses
*/
if ($response instanceof \Dingo\Api\Http\Response) {
$oldMeta = $response->getMeta();
$meta = array_merge($oldMeta, $request->user()->metadata());
$response->setMeta($meta);
}
return $response;
}
}
What I find is the Response at this point is no longer a Dingo API response, therefore I am unable to add metadata. I tried using the Dingo\Api\Http\Response::makeFromExisting() method to create a new response from the old request, I've also tried instantiating a new response but it appears that the Dingo Api response is processed before getting to the middleware.
What would be the most efficient way of adding the user-specific metadata to the response? Ideally I don't want to be adding it to every API endpoint individually.
Related
I want to send a request with or without 'Token' as a header.
If request has 'Token' as a header: if the user already has that item, it will return the item with the proper item_id of a specific user (based on its token), otherwise it will return null.
If request doesn't have 'Token' as a header: it will return the item with that item_id
I'm working with Zend Framework and in ItemResource I have this method:
public function fetch($id)
{
}
How can I check if my request has Token as a header or not and implement both cases inside fetch()?
Using Laminas API Tools it depends on wether you 're using a RPC or a REST resource. I will explain which tools the Laminas API Tools give you to evaluate the received header data.
You don 't have to reinvent the wheel, because Laminas API Tools has the received headers already at hand, when you 're in your fetch method.
Representational State Transfer (REST)
Rest resources normally extend the \Laminas\ApiTools\Rest\AbstractResourceListener class. This class listens for \Laminas\ApiTools\Rest\ResourceEvent. Fortunately, this event provides you with a request object that also contains the received header data.
<?php
declare(strict_types=1);
namespace Marcel\V1\Rest\Example;
use Laminas\ApiTools\Rest\AbstractResourceListener;
class ExampleResource extends AbstractResourceListener
{
public function fetch($id)
{
// requesting for an authorization header
$token = $this->getEvent()->getRequest()->getHeader('Authorization', null);
if ($token === null) {
// header was not received
}
}
}
As you can see the ResourceEvent returns a \Laminas\Http\Request instance when calling getRequest(). The request instance already contains all request headers you 've received. Just call getHeader with the given name and as second parameter a default value, which should be returned, when the header was not set. If there is no http_token header, you 'll get null as a result.
Remote Procedure Calls (RPC)
Since RPC requests are handled with a MVC controller class, you can get the request as easy as in a rest resource. Controller classes extend from \Laminas\Mvc\Controller\AbstractActionController, which already contains a request instance.
<?php
declare(strict_types=1);
namespace Marcel\V1\Rpc\Example;
use Laminas\Mvc\Controller\AbstractActionController;
class ExampleController extends AbstractActionController
{
public function exampleAction()
{
$token = $this->getRequest()->getHeader('Authorization', null);
if ($token === null) {
// token was not set
}
}
}
As you can see getting header data in rpc requests is as easy as in resource listeners. The procedure is the same because a request instance is also used here.
Conclusion
There is absolutely no need for coding things, that are already there. Just get the request instance from the event or the abstract controller and retrieve the header you want. Always keep in mind, that there are security aspects like CRLF injections, when dealing with raw data. The Laminas framework handles all this for you already.
Additionally you can check for all received headers by calling ->getHeaders() instead of ->getHeader($name, $default). You 'll get a \Laminas\Http\Header instance with all received headers.
You can get all HTTP header values by getallheaders() or just get the specific value by $_SERVER['HTTP_XXX'], in your case, replace XXX with Token, $_SERVER['HTTP_Token'].
Manual: https://www.php.net/manual/en/reserved.variables.server.php
public function fetch($id)
{
$token = $_SERVER['HTTP_Token'];
// do your busniess code
}
I'm working on a php application using Slim framework. My application homepage is making about 20 REST API calls, which is slowing down the page load.
I read that I can use Http Clients like Guzzle to call these API's asynchronously but I couldn't find any article that tells how to use Guzzle with Slim.
Can someone tell how to use Guzzle with Slim.
Or is there any other solution that can speed up the page load?
N.B: I'm a novice in PHP
To use Guzzle with Slim, you need to
Install it by running composer
$ composer require guzzlehttp/guzzle:~6.0
Guzzle installation
Guzzle Quickstart
Create dependency registration, for example
<?php
use GuzzleHttp\Client;
$container = $app->getContainer();
$container['httpClient'] = function ($cntr) {
return new Client();
};
and put it somewhere where it will be executed when index.php the main bootstrap file is loaded.
Then in your code, you can get guzzle instance from container
$guzzle = $container->httpClient;
For example if you have following route
$app->get('/example', App\Controllers\Example::class);
And controller Example as follow
<?php
namespace App\Controllers;
use GuzzleHttp\ClientInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
class Example
{
private $httpClient;
public function __construct(ClientInterface $httpClient)
{
$this->httpClient = $httpClient;
}
public function __invoke(Request $request, Response $response, array $args)
{
//call api, etc..etc
$apiResponse = $this->httpClient->get('http://api.blabla.org/get');
//do something with api response
return $response;
}
}
To inject guzzle instance to Example controller, you create its dependency registration
use App\Controllers\Example;
$container[Example::class] = function ($cntr) {
return new Example($cntr->httpClient);
}
To speed up your page load, if you are API developer then start from there. If you are not API developer and have no control, try to think if you can reduce number of API calls by removing non essential ones. Or as last resort, cache API call response to storage that is faster for your application to retrieve later.
For example using redis.
You calculate hash of API url call including its querystring and use hash as key to access cached API call response.
I would like to remove a specific cookie in a Guzzle response object.
My application uses Slim framework and I make calls to an API with Guzzle. Both Slim and Guzzle implement the Request and Response Interface (Psr7) so I can easily return a Guzzle response in a Slim controller like this :
class APIController {
public function call($request, $response) {
// Do stuff with $request (check body and params, change url, etc)
$client = new \GuzzleHttp\Client();
$response = $client->send($request, []);
return $response;
}
}
Everything works fine but the API returns a cookie I want to remove. I can remove the whole header with :
$response = $response->withoutHeader('Set-Cookie');
Is there a native way in Guzzle to remove a specific cookie by name instead of removing the whole header ?
I'm building a RESTful API using Symfony2, FOSRestBundle and an OAuth 2 server library.
For any given request, there are a number of possible responses and status codes that I can return to the client.
Take for example the process of getting a user:
<?php
class UserController extends CustomBaseController {
/**
* Get a user
* #Get("/users", name="users_get")
*/
public function getAction(Request $request) {
// Ensure a valid access token is present
$this->validAccessTokenOrExit();
$user = $this->getUser();
return $this->handleView($this->view($user, 200));
}
}
Ideally I would like to have the validAccessTokenOrExit() function terminate the request and return the 401 status code with an appropriate message. This means I can handle authentication in this function and reuse it in several actions across the API.
However I can't seem to find a way of terminating the response from another function, and I always have to return a Response from the action called.
Are there any clean solutions to accomplish this or something similar?
If you throw an exception that has the interface Symfony\Component\HttpKernel\Exception\HttpExceptionInterface (Symfony\Component\HttpKernel\Exception\HttpException for example) with the status code set (first parameter as 401 for HttpException) it will be handled by the HttpKernel in the way that you are expecting.
For example..
throw new HttpException(401, 'Something went wrong');
.. will be turned into a response with 401 as the status code.
You can use $this->createAccessDeniedException('message') within a controller.
This will create AccessDeniedException, terminate the current request and return a 403 with your message.
I did faced this situation and for time being I call the terminating function with return. Return false from function when valid.
if($r=$this->validAccessTokenOrExit()){
return $r;
}
Alternatively I use following two methods for redirecting or rendering a view form another function and terminating flow.
Redirection:
header('location:'. $this->generateUrl('deals_product_view', array('id' => 1)));
exit;
Rendering a view:
$response = new Response();
$response->setContent($this->renderView($view_file, $params ));
$response->send();
exit;
Is there a way to call a routine before each route in Slim PHP? I have a RESTful API and I want to validate the login before calling the API methods. My code looks like:
$app = new Slim();
$app->get('user/:id', function($id) use($app){
$user = API::getUser($id);
if($user){
$app->response->status(200);
}else{
$app->response->status(404);
}
});
The API makes the request and process a JSON response. I want to attach a precondition to allow the request, something like a callback. The API has a method API::validate($token) that returns true or false, I want to catch this and return status code 401 if authentication fails. Some methods like API::login() and API::register() don't need this validation.
First i strongly advice you to read ALL the documentation. It isn't that big and you'll get a good introduction to the framework. http://docs.slimframework.com/
You don't need a routine but a hook that gets called before each routine. That's the way slim is structured.
A easy solution would be to keep the non protected pages in a array, and in that hook check if the request is protected or not.
$app->hook('slim.before.dispatch', function() use ($app) {
$publicRoutes = array('login', 'welcome');
if(!in_array($app->router()->getCurrentRoute(), $publicRoutes)
// Get the token
$result = API::validate($token);
if(!$result) {
$app->redirect('/login');
}
});
If you want to handle a more complex process with permissions levels, oauths, etc you'll rather use a Middleware. I like them because it's the right way to do this kind of tasks with Slim, and you can reuse them.