Get Request Information from Promise Or Response Guzzle 6.0 - php

I want to get information about the request I have sent, such as url, body sent etc. I am using the Async feature which uses promises (example below)
$client = new \GuzzleHttp\Client();
return new \GuzzleHttp\Psr7\Request\Request('POST', $this->getUrl(), $this->getHeaders(), $this->getBody());
Is there a way where I can get request information from the promise or from the response?
Reason I am asking this is because I need to store some information about the request in the database later on, which can not be done before I sent the request.
What I tried so far is
Getting the information from the promise with the following methods
$promise->getRequest()
$pomise->Request
$promise->request
$promise->getHandlers()
Thank you

When you initialize a new Request then you have to send it. It's not sent by default. A request is sent when a Client calls send method on it. When request is completed you have access to all information about response:
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Client;
$client = new Client();
$request = new Request('GET', 'https://www.google.com');
$response = $client->send($request);
$response->getHeaders(); // array of all retreived headers
$response->getStatusCode(); // http status code
$response->getReasonPhrase(); // http status phrase
and you initialized a wrong Request object, Guzzle is not shipped with \GuzzleHttp\Psr7\Request\Request but \GuzzleHttp\Psr7\Request.
Now with a correct way of sending a request, getting request information is as simple as below:
print_r($request->getHeaders()); // array of all sent headers
echo $request->getUri(); // requested URI

Related

getHeader from getInternalResponse in Symfony Panther always return an empty array

By using Symfony Panther, I sent a request and I wanted to get the response. I was able to get the body and the status code but for the header I just got an empty array.
$client = Client::createChromeClient();
$client->request('GET', 'https://google.com');
$client->getInternalResponse()->getHeaders(); // returned empty array!!!
Panther does not have access to the HTTP response as explained in this github issue https://github.com/symfony/panther/issues/17.
But if you read carefuly, you'll see that this is not a limitation of Panther but a limitation of Selenium WebDriver. See this post How to get HTTP Response Code using Selenium WebDriver.
This means that the answer to the question "Can I have access to the HTTP response code or the HTTP header using Symfony Panther?" is "No, it's not possible".
While this is not possible the workaround I found was to create an HttpClient and use it to make a request and get the response from it.
<?php
use Symfony\Component\HttpClient\HttpClient;
$client = HttpClient::create();
$response = $client->request('GET', $this->myBaseUri);
$statusCode = $response->getStatusCode();
$headers = $response->getHeaders();
Here's the documentation for HTTP Client (Symfony Docs) if you want to try this way.
According to this issue: https://github.com/symfony/panther/issues/67, it seems that status code is not managed ( HTTP 200 will always get returned, no matter what the request actually responded.)
And same for the headers, I'm afraid. If you look at class
Symfony\Component\Panther\Client and method get($url) you can see that:
$this->internalResponse = new Response($this->webDriver->getPageSource());
while Response's constructor accepts:
public function __construct(string $content = '', int $status = 200, array $headers = [])
Having these said, no matter what happens, you always get HTTP 200 and empty header array.

Symfony 4.3 PHP: How to read the cookie from a 302 redirection response?

What I want to do (in the controller)
Receive a request, based on which
Send an external HTTP request
Parse the response, based on which
Return a response
I plan to have multiple functions that all follow this pattern. They're called one by one, in succession.
Symfony 4.3 introduces the HttpClient component and the BrowserKit component which at first sight seem to fit the bill. But as it turns out, things get more complicated. The external HTTP requests are sent to a server that tracks sessions with cookies, which shouldn't be anything too unusual. If the request is sent without cookies, the server always responds with the same landing page content. So we need the cookie. No worries, the response to the initial request comes with a "Set-Cookie" header that contains the cookie that is sent in the "Cookie" header in the subsequent requests. The HTTP status of the response with the cookie is "302 Found" which in a normal web environment, using a web browser, redirects to the next page with the same cookie in the request. Replicating this action in my Symfony controller seems to be very tricky. Expanding on step 2. of what I want to do now:
2.a. Send an external HTTP request
2.b. Read the cookie from the 302 response (without automatic redirection)
2.c. Set the "Cookie" header of the subsequent request, the value coming from 2.b.
2.d. Send the request to the new target (set in the "Location" header of the 302 response)
Here's some of the code from the controller:
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Exception\RedirectionException;
use Symfony\Component\HttpFoundation\JsonResponse;
public function init(Request $request): JsonResponse
{
$url = "external url";
$client = HttpClient::create();
try {
$response = $client->request('GET', $url, [
'max_redirects' => -1
]);
} catch (RedirectionException $redirectionException) {
}
return new JsonResponse([], 200);
}
Not setting max_redirects means that the client follows the redirects and ends up returning the 200 response without any cookies. Setting the value to -1 will throw a redirection
exception and again, the 302 response with the cookie seems to be lost.
I've also tried the HttpBrowser component:
$cookieJar = new CookieJar();
$client = new HttpBrowser(HttpClient::create(), null, $cookieJar);
$client->request('GET', $url);
But I get a 200 response without the cookie as it automatically follows the redirects.
I understand that the components mentioned here are new to Symfony 4.3 and "experimental" but I also don't think my task is overly complicated or unusual. But maybe there are other http clients for Symfony that would be better suited for the task?
As it turns out, this task was easier to complete by trying out other HTTP client components for Symfony than the native ones. So I've put Symfony HttpClient and HttpBrowser aside for now. I managed to implement a solution for this problem with guzzlehttp/guzzle version 6.3.3. Here's what it looks like in my controller function:
use GuzzleHttp\Client;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
public function init(Request $request): JsonResponse {
$client = new Client([
'base_uri' => 'an external domain',
'cookies' => true
]);
// get the 302 response, without following
$response = $client->get('path',[
'allow_redirects' => false
]);
// get the cookie from the 302 response
$jar = new \GuzzleHttp\Cookie\CookieJar(true,[$response->getHeader('Set-Cookie')]);
// follow the redirect manually, with the cookie from the 302 response
$redirectResponse = $client->request('GET', $response->getHeader('Location')[0], [
'cookies' => $jar
]);
$jsonResponse = new JsonResponse(['message' => 'my message'],200);
$originalCookie = Cookie::fromString($response->getHeader('Set-Cookie')[0]);
$newCookie = Cookie::create(
$originalCookie->getName(),
$originalCookie->getValue(),
$originalCookie->getExpiresTime(),
'/',
'my domain',
false,
true,
$originalCookie->isRaw(),
$originalCookie->getSameSite()
);
$jsonResponse->headers->setCookie($newCookie);
return $jsonResponse;
}
The cookie cannot be taken from the response as such or it will never be sent back because the domain or the path doesn't match. So I create a new cookie which is very similar to the original one, then send it back to the browser. This way the cookie comes back in the subsequent requests and the data content can be sent forward.
Other issues that arise here are the same-origin requirements which have to be taken care of on the external server.
I'm still interested in how to do all this with the native Symfony 4.3 components, or also, if anyone can confirm that it's not possible.

How to keep LineFeed in GuzzleHttp Response Body

I am doing and cUrl request using Guzzle.
$client = new GuzzleHttp\Client();
try {
$response = $client->get($url);
} catch (\GuzzleHttp\Exception\RequestException $ex) {
// handle error.
}
The server response is formatted like this:
"field1","field2","field3"\n
"value1","value2","value3"\n
"value4","","value5"\n
Using GuzzleHttp\Client, I receive the following response body
"field1","field2","field3""value1","value2","value3""value4","","value5"\n
Is it possible to set the Guzzle Client not to replace line feed chars in the response body?
We don't know what your response looks like.
We don't know how you are viewing the response.
In the presence of these two details I would have to guess that you might have to implement some type of stream decorator so that only single lines of the response are read in at a time.

Guzzle Maintaining Cookies

I'm creating an API for a site that allows the user to login via the API. I'm using Guzzle, but the question is how do I use the Cookies Plugin with Guzzle? In cURL I can use a cookie file, and pass this along with requests. But the example on the Guzzle docs looks confusing.
use Guzzle\Http\Client;
use Guzzle\Plugin\Cookie\CookiePlugin;
use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar;
$cookiePlugin = new CookiePlugin(new ArrayCookieJar());
// Add the cookie plugin to a client
$client = new Client('http://www.test.com/');
$client->addSubscriber($cookiePlugin);
// Send the request with no cookies and parse the returned cookies
$client->get('http://www.yahoo.com/')->send();
// Send the request again, noticing that cookies are being sent
$request = $client->get('http://www.yahoo.com/');
$request->send();
echo $request;
It seems to be making 3 requests. I don't understand why it's making a request to test.com, then twice to yahoo.com. Why are you not able to make 1 request?
It's just an example... You don't have to make three requests. Just attach the cookie plugin to your client and you're good.

Calling ASP.NET Web Api method with PHP

I have the following web method in my web api controller
public HttpResponseMessage PostMakeBooking(FacilityBookingRequest bookingRequest)
{
var returnStatus = HttpStatusCode.OK;
var json = new JavaScriptSerializer().Serialize(bookingRequest);
var response = Request.CreateResponse<CardholderResponse>(returnStatus, cardholderResponse);
return response;
}
When I make this call from my .NET app, my json string appears correctly when I seralize it
{"correlationId":null,"RequestId":"7ec5092a-342a-4e32-9311-10e7df3e3683","BookingId":"BK-123102","CardholderId":"123456","BookingFrom":"\/Date(1370512706448)\/","BookingUntil":"\/Date(1370523506449)\/","DeviceId":"ACU-01-R2","Action":"Add","LoginId":"tester","Password":"tester"}
However, when I made to call from my php script
public function web_request(){
$guid =self::getGUID();
$replace = array("{","}");
$guid = str_replace($replace, "", $guid);
$client = new Zend_Rest_Client("http://203.92.72.221");
$request= new myZendCommon_FacilityBookingRequest();
$request->RequestId =$guid;
$request->BookingFrom ="27/03/2013 05:30";
$request->BookingUntil ="27/03/2013 06:30";
$request->CardholderId ="E0185963";
$request->DeviceId ="ACU-B2-01-R1";
$request->BookingId ="111";
$request->Action ="Add";
$request->LoginId ="tester";
$request->correlationId ="(null)";
$request->Password ="tester";
$request = json_encode($request);
$response = $client->restPost("/ibsswebapi/api/facilitybooking",$request);
print_r($response);
exit();
The call goes to my web method, but when I serialize it using JavaScriptSerializer().Serialize(bookingRequest)
{"correlationId":null,"RequestId":null,"BookingId":null,"CardholderId":null,"BookingFrom":"\/Date(-62135596800000)\/","BookingUntil":"\/Date(-62135596800000)\/","DeviceId":null,"Action":null,"LoginId":null,"Password":null}
All the values are null.
Is something wrong with the script?
I believe Kiran is right. Not sure why some one has felt his answer is not useful. Anyways, my understanding is that you are creating a JSON string and doing a form post of the same. I guess in this case the content type is sent as application/www-form-urlencoded but request body is a JSON string. You can use Fiddler to see how the request is being sent by the PHP script. I don't have the PHP knowledge to tell you how you can post JSON but my guess is that if you just remove the JSON encoding line $request = json_encode($request);, it should be okay.
From ASP.NET Web API point of view, if the request has Content-Type: application/json header and the body has the right JSON or if the request has Content-Type:application/www-form-urlencoded header and the body has the form url encoded content like RequestId=7ec5092a-342a-4e32-9311-10e7df3e3683&BookingId=BK-123102 and so on, web API will absolutely have no problem in binding. Currently, the request is not being sent in the right format for web API to bind.
Are you sending the header Content-Type:application/json in your request?
Also add the following piece of code to catch any model state validation errors:
.
if (!ModelState.IsValid)
{
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState));
}

Categories