Specify raw body of a POST request with Guzzle - php

With Guzzle (version 3), I'd like to specify the body of a POST request in "raw" mode. I'm currently trying this:
$guzzleRequest = $client->createRequest(
'POST',
$uri,
null,
'un=one&deux=two'
);
But it kind of doesn't work. If I dump my $guzzleRequest I can see that postFields->data is empty. Using $guzzleRequest->setBody() afterwards doesn't help.
However if I specify the body as ['un'=>'one', 'deux'=>'two'], it works as expected.
How can I specify the body of the request as 'un=one&deux=two'?

First I would highly recommend that you upgrade to Guzzle 6 as Guzzle 3 is deprecated and EOL.
It has been a long time since I used Guzzle 3 but I do believe you want the following:
$request = $client->post(
$uri,
$header = [],
$params = [
'un' => 'one',
'deux' => 'two',
]);
$response = $request->send();
Guzzle will automatically set the Content-Type header.
More information is available with the Post Request Documentation.
In response to your comment:
$request = $client->post(
$uri,
$headers = ['Content-Type' => 'application/x-www-form-urlencoded'],
EntityBody::fromString($urlencodedstring)
)
For this, reference: EntityBody Source and RequestFactory::create()

Related

Laravel CURL - Passing a string to HTTP URL (Postfields)

I use Laravel 9 and the built-in method Http::withHeaders ($headers)->post($url, $data).
In the $data variable, I pass the string that resulted from http_build_request with the "&" separator. But Laravel tries to make an array out of it and send data.
The API service returns me an error. Please tell me how you can force Laravel to pass exactly a STRING(!), and not an array?
My code:
$array = [
'key1' => 'value1',
'key2' => 'value2',
// etc...
];
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'HMAC' => $hmac
];
$data = http_build_query($array, '', '&');
$response = Http::withHeaders($headers)->post($api_url, $data);
return json_decode($response->getBody()));
If you sending it as x-www-form-urlencoded i gues u should be able to pass the data inside request body.
$response = Http::withHeaders($headers)
->withBody($data)
->asForm()
->post($api_url);
however, i am not sure if this will work
Have you tried, asForm?
$response = Http::withHeaders($headers)->asForm()->post($api_url, $data);
If you would like to send data using the application/x-www-form-urlencoded content type, you should call the asForm method before making your request.

Guzzle 6 download file

Need help using Guzzle 6 for downloading a file from a rest API. I don't want the file to be saved locally but downloaded from web browser. Code so far below, but believe I am missing something?
<?php
//code for Guzzle etc removed
$responsesfile = $client->request('GET', 'documents/1234/content',
[
'headers' => [
'Cache-Control' => 'no-cache',
'Content-Type' => 'application/pdf',
'Content-Type' => 'Content-Disposition: attachment; filename="test"'
]
]
);
return $responsesfile;
?>
Just do research inside Guzzle's docs, for example here
Pass a string to specify the path to a file that will store the contents of the response body:
$client->request('GET', '/stream/20', ['sink' => '/path/to/file']);
Pass a resource returned from fopen() to write the response to a PHP stream:
$resource = fopen('/path/to/file', 'w');
$client->request('GET', '/stream/20', ['sink' => $resource]);
Pass a Psr\Http\Message\StreamInterface object to stream the response body to an open PSR-7 stream.
$resource = fopen('/path/to/file', 'w');
$stream = GuzzleHttp\Psr7\stream_for($resource);
$client->request('GET', '/stream/20', ['save_to' => $stream]);
stream_for is deprecated in version 7.2. You can use
GuzzleHttp\Psr7\Utils::streamFor($resource) instead.
First of all, Content-Type header only makes sense when you send something (POST/PUT), but not for GET requests.
Secondly, what is your issue? Guzzle by default does not store the response body (file) somewhere, so you can work with it inside your app, like $responsesfile->getBody().

Send Body as raw using Guzzle

I am trying to use Guzzle to send POST request to my web service. this service accepts body as raw. It works fine when I use postman but I doesn't using Guzzle. when using Guzzle, I get only the webservice description as I put the web service URL in the browser.
here is my code:
$body = "CA::Read:PackageItems (CustomerId='xxxxxx',AllPackages=TRUE);";
$headers = [
....
....
];
$client = new Client();
$response = $client->request('POST', 'http://172.19.34.67:9882/TisService',$headers,$body);
echo $body = $response->getBody();
seems headers or body doesn't pass through.
Try like this
$response = $client->request('POST', 'http://172.19.34.67:9882/TisService',['headers' => $headers, 'body' => $body]);
I have recently had to implement Guzzle for the first time and it is a fairly simple library to use.
First I created a new Client
// Passed in our options with just our base_uri in
$client = new Client(["base_uri" => "http://example.com"]);
I then created a POST request, not how I am using new Request instead of $client->request(... though. This doesn't really matter to much that I've used new Request though.
// Create a simple request object of type 'POST' with our remaining URI
// our headers and the body of our request.
$request = new Request('POST', '/api/v1/user/', $this->_headers, $this->body);
so in essence it would look like:
$request = new Request('POST', '/api/v1/user/', ['Content-Type' => "application/json, 'Accept' => "application/json"], '{"username": "myuser"}');
$this->headers is a simple key-value pair array of our request headers making sure to set the Content-Type header and $this->body is a simple string object, in my case it forms a JSON body.
I can simply then just call the $client->send(... method to send the request like:
// send would return us our ResponseInterface object as long as an exception wasn't thrown.
$rawResponse = $client->send($request, $this->_options);
$this->_options is a simple key-value pair array again simple to the headers array but this includes things like timeout for the request.
For me I have created a simple Factory object called HttpClient that constructs the whole Guzzle request for me this is why I just create a new Request object instead of calling $client->request(... which will also send the request.
What you essentially need to do to send data as raw is to json_encode an array of your $data and send it in the request body.
$request = new Request(
'POST',
$url,
['Content-Type' => 'application/json', 'Accept' => 'application/json'],
\GuzzleHttp\json_encode($data)
);
$response = $client->send($request);
$content = $response->getBody()->getContents();
Using guzzle Request GuzzleHttp\Psr7\Request; and Client GuzzleHttp\Client

Mock Slim endpoint POST requests with PHPUnit

I want to test the endpoints of my Slim application with PHPUnit. I'm struggling to mock POST requests, as the request body is always empty.
I've tried the approach as described here: Slim Framework endpoint unit testing. (adding the environment variable slim-input)
I've tried writing to php://input directly, but I've found out php://input is read only (the hard way)
The emulation of the environment works correctly as for example the REQUEST_URI is always as expected. I've found out that the body of the request is read out in Slim\Http\RequestBody from php://input.
Notes:
I want to avoid calling the controller methods directly, so I can test everything, including endpoints.
I want to avoid guzzle because it sends an actual request. I do not want to have a server running while testing the application.
my test code so far:
//inherits from Slim/App
$this->app = new SyncApiApp();
// write json to //temp, does not work
$tmp_handle = fopen('php://temp', 'w+');
fwrite($tmp_handle, $json);
rewind($tmp_handle);
fclose($tmp_handle);
//override environment
$this->app->container["environment"] =
Environment::mock(
[
'REQUEST_METHOD' => 'POST',
'REQUEST_URI' => '/1.0/' . $relativeLink,
'slim.input' => $json,
'SERVER_NAME' => 'localhost',
'CONTENT_TYPE' => 'application/json;charset=utf8'
]
);
//run the application
$response = $this->app->run();
//result: the correct endpoint is reached, but $request->getBody() is empty
Whole project (be aware that I've simplified the code on stackoverflow):
https://github.com/famoser/SyncApi/blob/master/Famoser.SyncApi.Webpage/tests/Famoser/SyncApi/Tests/
Note 2:
I've asked at the slimframework forum, link:
http://discourse.slimframework.com/t/mock-slim-endpoint-post-requests-with-phpunit/973. I'll keep both stackoverflow and discourse.slimframework up to date what is happening.
Note 3:
There is a currently open pull request of mine for this feature: https://github.com/slimphp/Slim/pull/2086
There was help over at http://discourse.slimframework.com/t/mock-slim-endpoint-post-requests-with-phpunit/973/7, the solution was to create the Request from scratch, and write to the request body.
//setup environment vals to create request
$env = Environment::mock();
$uri = Uri::createFromString('/1.0/' . $relativeLink);
$headers = Headers::createFromEnvironment($env);
$cookies = [];
$serverParams = $env->all();
$body = new RequestBody();
$uploadedFiles = UploadedFile::createFromEnvironment($env);
$request = new Request('POST', $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);
//write request data
$request->write(json_encode([ 'key' => 'val' ]));
$request->getBody()->rewind();
//set method & content type
$request = $request->withHeader('Content-Type', 'application/json');
$request = $request->withMethod('POST');
//execute request
$app = new App();
$resOut = $app($request, new Response());
$resOut->getBody()->rewind();
$this->assertEquals('full response text', $resOut->getBody()->getContents());
The original blog post which helped to answer was at http://glenneggleton.com/page/slim-unit-testing

GuzzlePHP mock response content

I want to mock a response to the Guzzle request:
$response = new Response(200, ['X-Foo' => 'Bar']);
//how do I set content of $response to--> "some mocked content"
$client = Mockery::mock('GuzzleHttp\Client');
$client->shouldReceive('get')->once()->andReturn($response);
I noticed I need to add as third parameter the interface:
GuzzleHttp\Stream\StreamInterface
but there are so many implementations of it, and I want to return a simple string. Any ideas?
Edit: now I use this:
$response = new Response(200, [], GuzzleHttp\Stream\Stream::factory('bad xml here'));
but when I check this:
$response->getBody()->getContents()
I get an empty string. Why is this?
Edit 2: this happened to me only when I used xdebug, when it runs normally it works great!
We'll just keep doing this. The previous answer is for Guzzle 5, this is for Guzzle 6:
use GuzzleHttp\Psr7;
$stream = Psr7\stream_for('{"data" : "test"}');
$response = new Response(200, ['Content-Type' => 'application/json'], $stream);
The previous answer is for Guzzle 3. Guzzle 5 uses the following:
<?php
$body = GuzzleHttp\Stream\Stream::factory('some mocked content');
$response = new Response(200, ['X-Foo' => 'Bar'], $body);
Using #tomvo answer and the comment from #Tim - this is what I did for testing Guzzle 6 inside my Laravel app:
use GuzzleHttp\Psr7\Response;
$string = json_encode(['data' => 'test']);
$response = new Response(200, ['Content-Type' => 'application/json'], $string);
$guzzle = Mockery::mock(GuzzleHttp\Client::class);
$guzzle->shouldReceive('get')->once()->andReturn($response);
Guzzle\Http\Message\Response allows you to specify the third parameter as a string.
$body = '<html><body>Hello world!</body></html>';
$response = new Response(200, ['X-Foo' => 'Bar'], $body);
If you'd prefer a solution that implements Guzzle\Stream\StreamInterface, then I recommend using Guzzle\Http\EntityBody for the most straightforward implementation:
$body = Guzzle\Http\EntityBody::fromString('<html><body>Hello world!</body></html>');
$response = new Response(200, ['X-Foo' => 'Bar'], $body);
For Guzzle 7, you can use the GuzzleHttp\Psr7\Utils::streamFor() method as follows:
$data = json_encode(['X-Foo' => 'Bar']);
$stream = Utils::streamFor($data);
And then you can pass the $stream object to the andReturn method of the mocked client.

Categories