Guzzle/Curl weird error when sending twice - php

I'm having problem sending on the second request. I tried to use Curl but the problem still persist.
I have a class that use guzzle to call to an existing api it look like this.
class HttpCall
{
public static function request($method, $endpoint, $payload)
{
$client = new Client();
$response = $client->request(...);
return $response;
}
}
Then I have a service class that use the HttpCall to fetch some data on another server
The flow is like this
Search Name (Request to another endpoint) > Update Data (Request to another endpoint)
so this two flow search name and update data each request a token to the server
Authenticate (Request to login and get token) > Search Name
Authenticate (Request to login and get token) > Update data
My service class is like this
class MyService
{
public function searchName($name)
{
$request = HttpCall::request(...);
return $request;
}
public function updateData($payload)
{
$request = HttpCall::request(...);
return $request;
}
}
then in my Class that actually interact with the events
class MyClass
{
public function __construct(MyService $service)
{
$this->service = $service;
}
public function update()
{
// When I remove this, It's working and hardcoded some data
$data = $this->service->searchName('test');
$updateData = [...];
$this->service->updateData($updateData);
}
}
I'm not quite sure what's happening why the request seem's to fail (sometimes)
Thanks for help guys

Related

Use a request header with HTTP Client to external Api server

Consider the following request to a Symfony controller:
http http://127.0.0.1:8000/index x-token:1000
#[Route('/index', name: 'index')]
public function index(HttpClientInterface $client, Request $request): Response
{
$client->request('GET', 'http://0.0.0.0:3001', ['headers' => ['x-token' => $request->headers->get('x-token')]]);
return new JsonResponse();
}
This code snippet is a minimal example for the usage in a controller. The controller accepts a Request, and uses the x-token header for authenticating against the 3rd Party Api (here: localhost:3001).
Is there a way, to automate this process? So basically - listen to incoming requests and inject the x-token header into a specific Scoped Client or the default client in Symfony.
The goal is, not to do this in every usage of the Http Client, but have a configured client service.
The client will be used all over the codebase, not just in a controller like in this minimal example.
I know that I can use Service Decoration and extend the clients in use. I fail how to connect the dots and make this work.
Have you tried using symfony kernel events?
First of all, if you are calling some 3rd-party api, I'd suggest you to create a separate class at the infrastructure layer, for example MyApiProvider. Using HttpClient right from your controller is not smart, because you may also want to adjust something (for example api host, etc). So it's gonna look like this:
<?php
namespace App\Infrastructure\Provider;
class MyApiProvider
{
// Of course, this also be better configurable via your .env file
private const HOST = 'http://0.0.0.0:3001';
private HttpClientInterface $client;
private ?string $token = null;
public function __construct(HttpClientInterface $client)
{
$this->client = $client;
}
public function setToken(string $token): void
{
$this->token = $token;
}
public function getSomething(): array
{
$response = $this->client->request(
'GET',
self::HOST,
['headers' => $this->getHeaders()]
);
return $response->toArray();
}
private function getHeaders(): array
{
$headers = [];
if ($this->token !== null) {
$headers['x-token'] = $this->token;
}
return $headers;
}
}
Then you need to use symfony's kernel.request event to inject token to your provider from the request:
<?php
namespace App\Event;
use Symfony\Component\HttpKernel\Event\KernelEvent;
class RequestTokenEventListener
{
private MyApiProvider $provider;
public function __construct(MyApiProvider $provider)
{
$this->provider = $provider;
}
public function onKernelController(KernelEvent $event): void
{
$request = $event->getRequest();
$token = $request->headers->get('x-token');
if ($token !== null) {
$this->provider->setToken($token);
}
}
}
And finally your controller:
#[Route('/index', name: 'index')]
public function index(MyApiProvider $provider): Response
{
$provider->getSomething();
return new JsonResponse();
}
So your provider is gonna have token context during each request, if the token is passed.

Send responseon EventSubscriber Symfony 3.4

I'm trying to set a response on an eventsubscriber that checks if an API authorization token it's correct
class TokenSubscriber implements EventSubscriberInterface
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if ($controller[0] instanceof TokenAuthenticatedController) {
$apiKey = $this->em->getRepository('AppBundle:ApiKey')->findOneBy(['enabled' => true, 'name' => 'apikey'])->getApiKey();
$token = $event->getRequest()->headers->get('x-auth-token');
if ($token !== $apiKey) {
//send response
}
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::CONTROLLER => 'onKernelController',
];
}
}
But I cant stop the current request and return a respone as a controller, what is the correct way to send a response with an error message and stop the current request
You can not do that using the FilterControllerEvent Event. On that moment, symfony already decided which controller to execute. I think you might want to look into the Symfony Security component. It can protect routes like what you want, but in a slightly different way (access_control and/or annotations).
If you want to block access to an API (eg. JSON), you easily follow this doc. You can also mix it using the Security annotations on your controllers or actions using this doc
I think you can throw an error here
throw new AccessDeniedHttpException('Your message here!');

Slim 3 no response

So this is what i had first:
$app->get('/object/{id:[0-9]+}', function ($request, $response, $args) {
$id = (int)$args['id'];
$this->logger->addInfo('Get Object', array('id' => $id));
$mapper = new ObjectMapper($this->db);
$object = $mapper->getObjectById($id);
return $response->withJson((array)$object);
});
It worked well and outputted the whole DB Object as a nice JSON String.
Now i reorganized everything a little on MVC basis and this is whats left:
$app->get('/object/{id:[0-9]+}', ObjectController::class . ':show')->setName('object.show');
It also works, but i don't get any Output. If i put a var_dump before the DB Object is there, but how do i get a JSON String from that again?
Here comes the Controller
<?php
namespace Mycomp\Controllers\Object;
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use Interop\Container\ContainerInterface;
use Mycomp\Models\Object;
class ObjectController
{
protected $validator;
protected $db;
protected $auth;
protected $fractal;
public function __construct(ContainerInterface $container)
{
$this->db = $container->get('db');
$this->logger = $container->get('logger');
}
public function show(Request $request, Response $response, array $args)
{
$id = (int)$args['id'];
$this->logger->addInfo('Get Object', array('id' => $id));
$object = new Object($this->db);
return $object->getObjectById($id);
}
}
As Nima said in comment, you need to return Response object
public function show(Request $request, Response $response, array $args)
...
return $response->withJson($object->getObjectById($id));
}
In order for Slim to send HTTP response to client, route callback must return some data that Slim understands. That type of data, according to Slim documentation is a PSR 7 Response object.
This is important, because what the route callback returns will not necessarily be sent to client exactly as is . It might be used by middlewares to teak the response before sending it to the client.
the $response object, injected by Slim into your route callbacks is used for that purpose. Slim also provides some helper methods like 'withJson` to generate a proper (PSR 7) JSON response with proper HTTP headers.
So as I said in comment, you need to return response object
public function show(Request $request, Response $response, array $args)
// Prepare what you want to return and
// Encode output data as JSON and return a proper response using withJson method
return $response->withJson($object->getObjectById($id));
}

How to pass instance of Request from a controller function to another controller function

I have to call a function from one controller an other controller.
public function getquickviews(Request $request){
$report = new ReportController();
$report ->Applications($request->except('cl_e_start_date'));//it's not working its giving me error that it expect and instance of Request and passed array()
}
public function Applications(Request $request)
{
/*APP USAGE*/
}
and I have to pass instance of Request to Application function. But the issue I don't wanted to pass all the parameter from getquickviews Request like if I am getting email,phone,name on the getquickviews function but I only have to pass phone,email to Application function.
You need to create a new instance of Request.
public function getquickviews(Request $request){
$report = new ReportController();
$content = new Request();
$content->something = $request->something;
$content->somethingElse = $request->somethingElse;
$report ->Applications($content);
}
and then you have to recieve it in:
public function Applications(Request $request)
{
/*APP USAGE*/
}
and that's it.
Regards.
Change this line
$report ->Applications($request->except('cl_e_start_date'));
To
$report ->Applications($request);
try as following (not sure it's gonna work) :
public function getquickviews(Request $request){
$returnedRequest = $request; // do whatever with your request here
return redirect()->route('SecondController.Applications', compact('returnedRequest'));
}
public function Applications(Request $request){
/*APP USAGE*/
}
To be able to create a custom request and thus use it to reference a post method in a controller, you need to first initiate an instance of Request as #martin Carrasco has described above:
the code below is a continuation of martin Carrasco
public function getquickviews(Request $request){
$report = new ReportController();
$content = new Request
([
'firstParam' => $request->param1,
'secondParam' => $request ->param2,
]);
$report ->Applications($content);
}
Try that n hope it works.
I think this will work :
$report ->Applications($request->toArray());
Two ways to get requests into next method or any next level call.
First you can inject Request class depenednacy into that method for an example:
public function store(Request $request)
{
// Form Submits here
}
If you want to pass $request into other method for example to display data after insert you can do this way:
public function showStore(Request $request)
{
dd($request->get());
}
Then you can call this method from store method
$this->showStore($request);
or second is you can use request as metho into showStore or any n level call. Like this:
public function showStore()
{
dd(request()->all());
}
$this->showStore(); // You do not require any injection.
Good Luck!!!
You can keep the particular key and value you want and delete the rest from the $request before passing it to the function. First convert the $request to array by
$request->toArray()
and then delete the unwanted keys by doing
unset($request['key-here']);
and then pass it to the function
$report ->Applications($request);

having trouble with slimframework's Immutable responses

I am trying to setup a project for an API using slim framework version 3, I don't know who made the PSR-7 and marked the response object as immutable, I don't see any use in that (IMHO. please explain me if I am wrong). Things were much easier when it was slim 2. Now I came back to slim after a long time.
I have a route which is a post method, I am getting data and saving it to the database and I am trying to send 201 as the response code. all the examples and the documentation is showing you how can you change the response code within the index.php file itself, But I am trying to change it from a response builder which I have tried to use the factory pattern to provide different responses. The problem is the response code always stays 200 no matter what function I call from the response builder class. I tried many forums and different ways of slim but still couldn't able to pull this up. I almost decided to give up on a PSR 7 router implementation and going to implement my own routing solution. But I remember not to reinvent the wheel again so I came here as a final try. Below is the code.
the route definition
$app->post('/users', function(ServerRequestInterface $req, ResponseInterface $res) {
$data = $req->getParsedBody();
$model = new \Apex\Models\User(ApexDB::getInstance());
$jsonBuilder = ApexResponse::getBuilder('JSON', $res);
$control = new \Apex\Controllers\User($model, $jsonBuilder);
$control->create($data);
});
the controller method (abstract I am just setting it up)
public function create($data) {
if($this->model->save($data)) {
$this->response->build($data,201);
} else {
$this->response->build('error',400);
}
}
the JSON builder
class JSONBuilder implements Response
{
public $response;
public function __construct($response)
{
$this->response = $response;
}
public function build($data, $status)
{
$response = $this->response->withJSON($data,$status);
return $response;
}
}
can anyone point me in the right direction?
The PSR-7 decision to use immutable objects for Request and Response is documented in the Why value objects? section of the Meta document.
With Slim 3, you must always return a Response instance from the controller method.
$app->post('/users', function(ServerRequestInterface $req, ResponseInterface $res) {
$data = $req->getParsedBody();
$model = new \Apex\Models\User(ApexDB::getInstance());
$jsonBuilder = ApexResponse::getBuilder('JSON', $res);
$control = new \Apex\Controllers\User($model, $jsonBuilder);
return $control->create($data);
});
and then your create method also needs to return the $response:
public function create($data) {
if($this->model->save($data)) {
$this->response->build($data,201);
} else {
$this->response->build('error',400);
}
return $this->response;
}
It should then work.
However, you can use the controller method directly from the route declaration and avoid the need for a the closure:
$app->post('/users', `Apex\Controllers\User::create`);
The controller's create method would then look like this:
namespace Apex\Controllers;
class User
{
public function create($request, $response)
{
$data = $request->getParsedBody();
$model = new \Apex\Models\User(ApexDB::getInstance());
$jsonBuilder = ApexResponse::getBuilder('JSON', $response);
if ($model->save($data)) {
$response = $jsonBuilder->build($data, 201);
} else {
$response = $jsonBuilder->build('error', 400);
}
return $response;
}
}
Finally, consider rka-content-type-renderer instead of JsonBuilder, though maybe it does more than you've shown.
Update:
Ideally you'd use constructor injection to inject the User model into the controller. To do this:
Update your controller:
namespace Apex\Controllers;
use Apex\Models\User as UserModel;
class User
{
protected $userModel;
public function __construct(UserModel $userModel)
{
$this->userModel = $userModel;
}
public function create($request, $response)
{
$data = $request->getParsedBody();
$jsonBuilder = ApexResponse::getBuilder('JSON', $response);
if ($this->userModel->save($data)) {
$response = $jsonBuilder->build($data, 201);
} else {
$response = $jsonBuilder->build('error', 400);
}
return $response;
}
}
Write a factory for the Pimple dependency injection container:
$container = $app->getContainer();
$container['Apex\Controllers\User'] = function ($c) {
$userModel = new \Apex\Models\User(ApexDB::getInstance());
return new \ApexController\User($userModel);
};

Categories