ZF2 - Cookie exception header when posting to remote url - php

I am trying to post a form via cURL from inside a zend framework 2 console route.
I am doing something like
php public/index.php myroute
To call the route from terminal. Inside that route is the following:
$request = new \Zend\Http\Request;
$request->setMethod('POST');
$request->setUri('http://somesite.com');
$request->getHeaders()->addHeaders([
'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'
]);
/* set post fields */
$aData = array('hello' => 'world');
$request->setContent($request->getPost()->toString());
/* just to look at request string in console */
echo $request->toString()."\n";
/* post the data */
$client = new \Zend\Http\Client;
$client->setAdapter("Zend\Http\Client\Adapter\Curl");
$response = $client->dispatch($request);
Problem is that this throws a cookie exception when ran from console:
POST http://somesite.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
hello=world
======================================================================
The application has thrown an exception!
======================================================================
Zend\Http\Header\Exception\InvalidArgumentException
Cookie name cannot contain these characters: =,; \t\r\n\013\014 (WEBUK=)
If anyone can point out how to get around this I would be very grateful.
The code works in a normal route OKAY...it only complains about the cookie issue when I run it from console.

Fixed my issue for now. I have decided to give up on using zf2 methods for this and instead just use Guzzle.
$aData = array('hello' => 'world');
$httpClient = new GuzzleHttp();
$r = $httpClient->post('http://somesite.com', [
'body' => $aData
]);
$response = $httpClient->send($r);

Related

Guzzle PSR7 request with multipart/form-data

I created a simple API in Lumen (application A) which:
receives PSR-7 request interface
replaces URI of the request to the application B
and sends the request through Guzzle.
public function apiMethod(ServerRequestInterface $psrRequest)
{
$url = $this->getUri();
$psrRequest = $psrRequest->withUri($url);
$response = $this->httpClient->send($psrRequest);
return response($response->getBody(), $response->getStatusCode(), $response->getHeaders());
}
The above code passes data to the application B for the query params, x-www-form-urlencoded, or JSON content type. However, it fails to pass the multipart/form-data. (The file is available in the application A: $psrRequest->getUploadedFiles()).
Edit 1
I tried replacing the Guzzle invocation with the Buzz
$psr18Client = new Browser(new Curl(new Psr17Factory()), new Psr17Factory());
$response = $psr18Client->sendRequest($psrRequest);
but still, it does not make a difference.
Edit 2
Instances of ServerRequestInterface represent a request on the server-side. Guzzle and Buzz are using an instance of RequestInterface to send data. The RequestInterface is missing abstraction over uploaded files. So files should be added manually http://docs.guzzlephp.org/en/stable/request-options.html#multipart
$options = [];
/** #var UploadedFileInterface $uploadedFile */
foreach ($psrRequest->getUploadedFiles() as $uploadedFile) {
$options['multipart'][] = [
'name' => 'file',
'fileName' => $uploadedFile->getClientFilename(),
'contents' => $uploadedFile->getStream()->getContents()
];
}
$response = $this->httpClient->send($psrRequest, $options);
But still no luck with that.
What I am missing? How to change the request so files will be sent properly?
It seems that $options['multipart'] is taken into account when using post method from guzzle. So changing the code to the $response = $this->httpClient->post($psrRequest->getUri(), $options); solves the problem.
Also, it is important not to attach 'content-type header.

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

How to add form data on Post requests for Buzz HTTP Client on Laravel?

I'm using Buzz HTTP Client for Laravel.
I have a problem adding form data to my POST requests, since it wasn't specified in it's wiki/documentation.
Listed below are the two ways of sending requests.
Example 1:
$response = Buzz::post('http://api.website.com/login');
//how do I add a "username", and "password" field in my POST request?
echo $response;
echo $response->getContent;
Example 2:
$request = new Buzz\Message\Request('POST', '/', 'http://google.com');
$response = new Buzz\Message\Response();
//how do I add a "username", and "password" field in my POST request?
$client = new Buzz\Client\FileGetContents();
$client->send($request, $response);
echo $request;
echo $response;
The answer here is going to really depend on what the API expects. Lets assume, the API expects the password and username sent as JSON in the content of the request. The example http request would look something like:
POST /login HTTP/1.1
Content-Type: application/json
{
"username": "bugsBunny",
"password": "wh4tsUpD0c"
}
To do this with Buzz, this should work:
$jsonPayload = json_encode([
‘username’ => ‘bugsBunny’,
‘password’ => ‘wh4tsUpD0c
]);
$headers = ['Content-Type', 'application/json'];
$response = Buzz::post('http://api.website.com/login', $headers, $jsonPayload);
If you're attempting to submit a form on a given website, you shouldn't use the above method. Instead use Buzz's built in form method which will attach the correct headers.
use Buzz\Message\Form;
$request = new Form(Form::METHOD_POST, ‘login’, ‘api.website.com’);
$request->setFields([
‘username’ => ‘bugsBunny’,
‘password’ => ‘wh4tsUpD0c’
]);
$response = new Buzz\Message\Response();
$client = new Buzz\Client\Curl();
$client->send($request, $response);
On a side note, I'd suggest not using this library. The library is, as you stated, Laravel integration for Buzz. The issue here is, the author should have made buzz a dependency listed in composer, rather than include the Buzz source directly. This prevents updates to Buzz from making their way into this project. You can see on the actual Buzz repo, the last commit was 29 days ago. Also if another package is using Buzz and including it correctly by composer, composer would install both packages. But when an instance of Buzz was created, you couldn't be certain which version was being loaded. You should just use Buzz, which can be found on packagist.
// assuming $headers and $jsonPayload are the same as in previous example.
$browser = new Buzz\Browser();
$response = $browser->post('http://api.website.com/login', $headers, $jsonPayload);
It was foolish of me to not read the code first before asking.
The form data is actually pased on the third parameter for the function. Though it accepts strings only so don't forget to json encode your data.
Buzz Class
public function post($url, $headers = array(), $content = '')
{
....
....
}
Buzz::post($url, array(), json_encode(array('Username'=>'usernamexx','Password'=>'p#$$w0rD')) );

Laravel - POST data is null when using external request

I'm new to laravel, and I'm trying to implement a simple rest api.
I have the controller implemented, and tested via unit testing.
My problem is with the POST request.
Via the tests Input:json has data, via an external rest client it returns null.
This is the code on the unit test
$newMenu = array(
'name'=>'Christmas Menu',
'description'=>'Christmas Menu',
'img_url'=>'http://www.example.com',
'type_id'=>1,
);
Request::setMethod('POST');
Input::$json = $newMenu;
$response = Controller::call('menu#index');
What am I doing wrong?
UPDATE:
This is realy driving me crazy
I've instanciated a new laravel project and just have this code:
Routes
Route::get('test', 'home#index');
Route::post('test', 'home#index');
Controller:
class Home_Controller extends Base_Controller {
public $restful = true;
public function get_index()
{
return Response::json(['test'=>'hello world']);
}
public function post_index()
{
return Response::json(['test'=>Input::all()]);
}
}
CURL call:
curl -H "Accept:application/json" -H"Content-type: application/json" -X POST -d '{"title":"world"}' http://localhost/laravel-post/public/test
response:
{"test":[]}
Can anyone point me to what is wrong.
This is really preventing me to use laravel, and I really liked the concept.
Because you are posting JSON as your HTTP body you don't get it with Input::all();
You should use:
$postInput = file_get_contents('php://input');
$data = json_decode($postInput, true);
$response = array('test' => $data);
return Response::json($response);
Also you can use
Route::any('test', 'home#index');
instead of
Route::get('test', 'home#index');
Route::post('test', 'home#index');
Remove header Content-type: application/json if you are sending it as key value pairs and not a json
If you use : Route::post('test', 'XYZController#test');
Send data format : Content-type : application/json
For example : {"data":"foo bar"}
And you can get the post (any others:get, put...etc) data with :
Input::get('data');
This is clearly written in here : http://laravel.com/docs/requests
. Correct Content-type is very important!
I am not sure your CURL call is correct. Maybe this can be helpful : How to POST JSON data with Curl from Terminal/Commandline to Test Spring REST?
I am using Input::get('data') and it works.
I was facing this problem, my response of post was always null. To solve that I put the body key in guzzle object, like this
$client = new Client([
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => config('app.callisto_token'),
]
]);
$body = [
'firstResult'=> 0,
'data' => '05/05/2022'
];
$response = $client->post('http://'.$this->ip.'/IntegracaoERP'.'/status_pedido',
['body' => json_encode($body)]
);
Don't forget the json_encode in body key.
Hope this helps.

Categories