25

I am using Guzzle (v6.1.1) in PHP to make a POST request to a server. It works fine. I am adding some logging functions to log what was sent and received and I can't figure out how to get the data that Guzzle sent to the server. I can get the response just fine, but how do I get the sent data? (Which would be the JSON string.)

Here is the relevant portion of my code:

$client = new GuzzleHttp\Client(['base_uri' => $serviceUrlPayments ]);
    try {
       $response = $client->request('POST', 'Charge', [
            'auth' => [$securenetId, $secureKey],
            'json' => [     "amount" => $amount,
                            "paymentVaultToken" => array(
                                    "customerId" => $customerId,
                                    "paymentMethodId" => $token,
                                    "publicKey" => $publicKey
                                    ),
                            "extendedInformation" => array(
                                    "typeOfGoods" => $typeOfGoods,
                                    "userDefinedFields" => $udfs,
                                    "notes" => $Notes
                                    ),
                            'developerApplication'=> $developerApplication 
            ]
    ]);

    } catch (ServerErrorResponseException $e) {
        echo (string) $e->getResponse()->getBody();
    }


    echo $response->getBody(); // THIS CORRECTLY SHOWS THE SERVER RESPONSE
    echo $client->getBody();           // This doesn't work
    echo $client->request->getBody();  // nor does this

Any help would be appreciated. I did try to look in Guzzle sourcecode for a function similar to getBody() that would work with the request, but I'm not a PHP expert so I didn't come up with anything helpful. I also searched Google a lot but found only people talking about getting the response back from the server, which I have no trouble with.

ruhnet
  • 651
  • 1
  • 7
  • 14

4 Answers4

26

You can do this work by creating a Middleware.

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;

$stack = HandlerStack::create();
// my middleware
$stack->push(Middleware::mapRequest(function (RequestInterface $request) {
    $contentsRequest = (string) $request->getBody();
    //var_dump($contentsRequest);

    return $request;
}));

$client = new Client([
    'base_uri' => 'http://www.example.com/api/',
    'handler' => $stack
]);

$response = $client->request('POST', 'itemupdate', [
    'auth' => [$username, $password],
    'json' => [
        "key" => "value",
        "key2" => "value",
    ]
]);

This, however, is triggered before to receive the response. You may want to do something like this:

$stack->push(function (callable $handler) {
    return function (RequestInterface $request, array $options) use ($handler) {
        return $handler($request, $options)->then(
            function ($response) use ($request) {
                // work here
                $contentsRequest = (string) $request->getBody();
                //var_dump($contentsRequest);
                return $response;
            }
        );
    };
});
Federkun
  • 36,084
  • 8
  • 78
  • 90
  • Both of these causes something to go wrong and I get a 404 from the server. – ruhnet Jan 20 '16 at 21:58
  • I've just tried (guzzle 6.1), and it works for me. Can you post your full code? – Federkun Jan 20 '16 at 22:03
  • I have edited my question to show the actual code except for variable assignments and such that aren't relevant. I tried what you posted by adding all your middleware code before my code, and adding the `'handler' => $stack` in the client object creation command. – ruhnet Jan 20 '16 at 22:29
  • maybe the problem is one of the missing middleware. can you try now? – Federkun Jan 20 '16 at 22:41
  • What do you mean by "missing middleware"? I don't understand. I thought the middleware was the code you posted for me to try? – ruhnet Jan 20 '16 at 22:47
  • 1
    by default guzzle use [four middleware](https://github.com/guzzle/guzzle/blob/master/src/HandlerStack.php#L41). See my edit. Create the stack with `HandlerStack::create();`. – Federkun Jan 20 '16 at 22:52
  • 5
    Thanks. To guzzle, this way too complicated for what should be a simple task – user697576 Nov 03 '17 at 18:53
  • This code CREATES the instance of Guzzle\HttpClient. Is there an alternative way to ADD a middleware to an EXISTING instance of Guzzle\HttpClient, e.g. one received as an argument? – Szczepan Hołyszewski Jun 19 '18 at 14:33
18

Using Guzzle 6.2.

I've been struggling with this for the last couple days too, while trying to build a method for auditing HTTP interactions with different APIs. The solution in my case was to simply rewind the request body.

The the request's body is actually implemented as a stream. So when the request is sent, Guzzle reads from the stream. Reading the complete stream moves the stream's internal pointer to the end. So when you call getContents() after the request has been made, the internal pointer is already at the end of the stream and returns nothing.

The solution? Rewind the pointer to the beginning and read the stream again.

<?php
// ...
$body = $request->getBody();
echo $body->getContents(); // -->nothing

// Rewind the stream
$body->rewind();
echo $body->getContents(); // -->The request body :)
DJ Sipe
  • 1,286
  • 13
  • 12
2

My solution for Laravel from 5.7:

MessageFormatter works with variable substitutions, see this: https://github.com/guzzle/guzzle/blob/master/src/MessageFormatter.php

     $stack = HandlerStack::create();
        $stack->push(
            Middleware::log(
                Log::channel('single'),
                new MessageFormatter('Req Body: {request}')
            )
     );

    $client = new Client();
    $response = $client->request(
        'POST',
        'https://url.com/go',
        [
            'headers' => [
                "Content-Type" => "application/json",
                'Authorization' => 'Bearer 123'
            ],
            'json' => $menu,
            'handler' => $stack
        ]
    );
-2

You can reproduce the data string created by the request by doing

$data = array(
    "key" => "value",
    "key2" => "value",
); 

$response = $client->request('POST', 'itemupdate', [
    'auth' => [$username, $password],
    'json' => $data,
]);

// ...

echo json_encode($data);

This will output your data as JSON string.

Documentation at http://php.net/manual/fr/function.json-encode.php

EDIT

Guzzle has a Request and a Response class (and many other).
Request has effectively a getQuery() method that returns an object containing your data as private, same as all other members.
Also you cannot access it.

This is why I think manually encode it is the easier solution. If you want know what is done by Guzzle, it also have a Collection class that transform data and send it in request.

chalasr
  • 12,971
  • 4
  • 40
  • 82
  • 1
    Yes, I knew I could do it this way, but hoped there was a built in function in Guzzle that would allow me to get the request, just like I can get the response with `$response->getBody();` – ruhnet Jan 20 '16 at 21:48
  • Ah, yes I tried getQuery() but it didn't work and so if it's private that is why. Thanks for clarifying that for me. – ruhnet Jan 20 '16 at 22:05