Odd problem with PHP Curl and guzzle in Laravel - php

I'm trying to interact with a WS. This WS accept post requests in multipart and also in x-www-urlencoded. The WS is an OCR service, you send a passport picture and the WS returns a json with the MRZ data.
When I try the WS with postman always works, I send any photo of any size (I tested with 5Mb photo) and always works. When I send the exactly the same data using Curl or Guzzle, if the photo is about 30-50Kb the WS works as expected, but when I send a larger image, for example 500Kb, then the WS doesn't work.
I can access to the Ws code because it's propietary. I just trying to understand why is this happen. This is the multipart function
$url='http://10.0.20.113:8080/';
$req = new Client(['verify' => 0]);
$response = $req->post(
$url, [
'headers' => [
'accept-encoding' => 'gzip, deflate, br',
'accept' => '*/*'
],
'multipart' => [
[
'name' => 'user',
'contents' => 'admin'
],
[
'name' => 'pwd',
'contents' => 'admin'
],
[
'name' => 'doctype',
'contents' => '0'
],
[
'name' => 'new',
'contents' => '1'
],
[
'name' => 'pressmode',
'contents' => '2'
],
[
'name' => 'image1',
'contents' => $img1
],
[
'name' => 'image2',
'contents' => $img2,
]
],
]
);
Log::info('Response received');
return $response->getBody()->getContents();
this is another try with curl
$ch = curl_init();
$headers = array();
$post = array('user' => 'admin', 'pwd' => 'admin', 'doctype' => 0, 'new' => 1, 'pressmode' => 2, 'image1' => $img1, 'image2' => $img2); // e.g. $fieldName = 'image'
curl_setopt_array(
$ch,
[
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_URL => $url,
CURLOPT_POSTFIELDS => $post,
CURLOPT_SSL_VERIFYPEER => 0
]
);
$result = curl_exec($ch);
$response = curl_getinfo($ch);

Finally it was a apache issue in the WS side. Using curl, when the request size is more than 1024 bytes, then curl send a expect:100-continue header.
When the server receives this header, it should respond with an http 100 code, indicating to the client that it can continue transmission.
Postman doesn't use this header even with large requests.
The WS use a embedded apache to serve the requests, which I presume that has a bug (there is at least to bugs related to that) that makes apache doesn't return the http 100 code. Therefore curl aborts the request.

Related

Issue converting from cURL to Guzzle 6 with JSON and XML file

I am having a hard time converting cURL to Guzzle6. I'm want to send a name and reference UUID via JSON AND the contents of an XML file to process to a REST endpoint.
cURL
curl -H 'Expect:' -F
'request={"name":"test", "reference":"870e0320-021e-4c67-9169-d4b2c7e5b9c9"}'
-F 'file=#sample.xml' http://ec2-48-88-173-9.us-east-1.compute.amazonaws.com:8180/rs/process
Guzzle
$client = new Client(['debug' => true]);
$request = $client->request('POST',
'http://ec2-48-88-173-9.us-east-1.compute.amazonaws.com:8180/rs/process', [
'multipart' => [
[
'name' => 'data',
'contents' => "{'name':'test','reference':870e0320-021e-4c67-9169-d4b2c7e5b9c9}",
'headers' => ['Content-Type' => 'application/json']
],
[
'name' => 'file',
'contents' => fopen('sample.xml', 'r'),
'headers' => ['Content-Type' => 'text/xml']
],
]
]
);
$response = $request->getBody()->getContents();
Also, I'm not sure what the 'name' fields should be ('name' => 'data'), etc.
This is the Guzzle equivalent of your curl command:
$client = new Client(['debug' => true]);
$request = $client->request('POST',
'http://ec2-48-88-173-9.us-east-1.compute.amazonaws.com:8180/rs/process', [
'multipart' => [
[
'name' => 'request',
'contents' => "{'name':'test','reference':870e0320-021e-4c67-9169-d4b2c7e5b9c9}",
],
[
'name' => 'file',
'contents' => fopen('sample.xml', 'r'),
],
]
]
);
$response = $request->getBody()->getContents();
For the file Guzzle will specify the appropriate content type, as curl does. Name for the first part is request — from -F
'request={"name":"test", "reference":"870e0320-021e-4c67-9169-d4b2c7e5b9c9"}'

Upload file using Guzzle

I have a form where video can be uploaded and sent to remote destination. I have a cURL request which I want to 'translate' to PHP using Guzzle.
So far I have this:
public function upload(Request $request)
{
$file = $request->file('file');
$fileName = $file->getClientOriginalName();
$realPath = $file->getRealPath();
$client = new Client();
$response = $client->request('POST', 'http://mydomain.de:8080/spots', [
'multipart' => [
[
'name' => 'spotid',
'country' => 'DE',
'contents' => file_get_contents($realPath),
],
[
'type' => 'video/mp4',
],
],
]);
dd($response);
}
This is cURL which I use and want to translate to PHP:
curl -X POST -F 'body={"name":"Test","country":"Deutschland"};type=application/json' -F 'file=#C:\Users\PROD\Downloads\617103.mp4;type= video/mp4 ' http://mydomain.de:8080/spots
So when I upload the video, I want to replace this hardcoded
C:\Users\PROD\Downloads\617103.mp4.
When I run this, I get an error:
Client error: POST http://mydomain.de:8080/spots resulted in a 400 Bad Request response: request body invalid: expecting form value 'body`'
Client error: POST http://mydomain.de/spots resulted in a 400 Bad Request response:
request body invalid: expecting form value 'body'
I'd review the Guzzle's multipart request options. I see two issues:
The JSON data needs to be stringified and passed with the same name you're using in the curl request (it's confusingly named body).
The type in the curl request maps to the header Content-Type. From $ man curl:
You can also tell curl what Content-Type to use by using 'type='.
Try something like:
$response = $client->request('POST', 'http://mydomain.de:8080/spots', [
'multipart' => [
[
'name' => 'body',
'contents' => json_encode(['name' => 'Test', 'country' => 'Deutschland']),
'headers' => ['Content-Type' => 'application/json']
],
[
'name' => 'file',
'contents' => fopen('617103.mp4', 'r'),
'headers' => ['Content-Type' => 'video/mp4']
],
],
]);
While using the multipart option, make sure you are not passing content-type => application/json :)
If you want to POST form fields AND upload file together, just use this multipart option. It's an array of arrays where name is form field name and it's value is the POSTed form value. An example:
'multipart' => [
[
'name' => 'attachments[]', // could also be `file` array
'contents' => $attachment->getContent(),
'filename' => 'dummy.png',
],
[
'name' => 'username',
'contents' => $username
]
]

Php, Guzzle, Schema validation failed but working in curl

I'm in the process of convert from cURL to Guzzle, and got most of it working.
GET requests working great etc.
My problem is the POST request, getting Schema validation errors.
It works in curl, so I'm a bit confused... well, a lot.
Client error: `POST https://restapi.e-conomic.com/customers` resulted in a `400 Bad Request` response:
{"message":"Schema validation failed.","errorCode":"E00500"
I hope one of you can tell me, if I did something wrong in the convert? Maybe my arrays needs to be formatted in another way.
This is my old working "cURL code":
$data = array(
'name' => 'Test User',
'address' => 'Road 123',
'email' => 'morten#domain.com',
'zip' => '9000',
'city' => 'City',
'country' => 'Danmark',
'corporateIdentificationNumber' => '12345678',
'customerGroup' => array(
'customerGroupNumber' => 1
),
'currency' => 'DKK',
'paymentTerms' => array(
'paymentTermsNumber' => 1
),
'vatZone' => array(
'vatZoneNumber' => 1
)
);
$options = array(
CURLOPT_URL => 'https://restapi.e-conomic.com/customers',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => array(
'X-AppSecretToken:[removed]',
'X-AgreementGrantToken:[removed]',
'Content-Type:application/json; charset=utf-8'
),
CURLOPT_POSTFIELDS => json_encode($data)
);
curl_setopt_array($ch, $options);
This is my new "guzzle code", that is causing me problems:
$client = new GuzzleHttp\Client();
$headers = [
'X-AppSecretToken' => '[removed]',
'X-AgreementGrantToken' => '[removed]',
'Content-Type' => 'application/json;charset=utf-8',
'debug' => false
];
$form_params = [
'name' => 'Test User',
'address' => 'Road 123',
'email' => 'test#email.dk',
'zip' => '9000',
'city' => 'City',
'country' => 'Danmark',
'corporateIdentificationNumber' => '12345678',
'customerGroup' => [
'customerGroupNumber' => 1
],
'currency' => 'DKK',
'paymentTerms' => [
'paymentTermsNumber' => 1
],
'vatZone' => [
'vatZoneNumber' => 1
]
];
$response = $client->post('https://restapi.e-conomic.com/customers', [
'headers' => $headers,
'form_params' => $form_params
]);
I tried to use the "body" parameter, as stated in the Guzzle documentation, but received this error:
Passing in the "body" request option as an array to send a POST request has been deprecated. Please use the "form_params" request option to send a application/x-www-form-urlencoded request, or the "multipart" request option to send a multipart/form-data request.
I'm not sure what to do and really hope, that one of you guys will tell me what i'm doing wrong.
https://restapi.e-conomic.com/schema/customers.post.schema.json#_ga=2.167601086.1488491524.1500877149-796726383.1499933074
https://restdocs.e-conomic.com/#post-customers
I had to post the quest as json:
$response = $client->post('https://restapi.e-conomic.com/customers', [
'headers' => $headers,
'json' => $form_params
]);

guzzle, how to force content-type in a multipart/form-data

I'm new with Guzzle and I'm trying to make a REST request to sign PDF file. The provider says :
you need to use BASIC authentication
the request must be a POST request
the mimetype should be multipart/form-data
the file sent must be application/octet-stream and its name should be "file"
the data sent must be application/json and its name should be "data"
The system returns a response which contains the signed PDF file and type is application/octet-stream
This is the code I tested with Guzzle, but the provider says that the type mime sent in application/pdf. How can I "force" the mimetype for the PDF file ?
$client = new Client([
'auth' => ['login', 'password'],
'debug' => true,
'curl' => [
CURLOPT_PROXY => '192.168.1.232',
CURLOPT_PROXYPORT => '8080',
CURLOPT_PROXYUSERPWD => 'username:password',
],
]);
$boundary = 'my_custom_boundary';
$multipart = [
[
'name' => 'data',
'contents' => "{'nomDocument':'documentTest.pdf','externalid':'123456'}",
'Content-Type' => 'application/json'
],
[
'name' => 'file',
'contents' => fopen('documentTest.pdf', 'r'),
'Content-Type' => 'application/octet-stream'
],
];
$params = [
'headers' => [
'Connection' => 'close',
'Content-Type' => 'multipart/form-data; boundary='.$boundary,
],
'body' => new GuzzleHttp\Psr7\MultipartStream($multipart, $boundary),
];
try{
$response = $client->request('POST', 'https://server.com/api/sendDocument', $params);
} catch (RequestException $e) {
echo Psr7\str($e->getRequest());
if ($e->hasResponse()) {
echo Psr7\str($e->getResponse());
}
}
Thank you for your help.
You have to pass the Content-Type in headers
$multipart = [
[
'name' => 'data',
'contents' => "{'nomDocument':'documentTest.pdf','externalid':'123456'}",
'headers' => [ 'Content-Type' => 'application/json']
],
[
'name' => 'file',
'contents' => fopen('documentTest.pdf', 'r'),
'headers' => [ 'Content-Type' => 'application/octet-stream']
],
];
in Guzzle Documentation say that you can specify headers for every multipart data.
If you not set header Guzzle put a Content-Type for you based on file.

Guzzle form_params not accepting array

I am using Guzzle 6 and I can't pass array with form_params in the body of the client
$postFields = [
form_params => [
'data[test]' => "TEST",
'data[whatever]' => "Whatever..."
]
];
$client = new GuzzleClient([
'cookies' => $jar, // The cookie
'allow_redirects' => true, // Max 5 Redirects
'base_uri' => $this->navigateUrl, // Base Uri
'headers' => $this->headers
]);
$response = $client->post('api',[$postFields]);
Finally, when i send the request my data is gone... But if I manually add the the data in the response it's working fine.
$response = $client->post(
'api',
[form_params => [
'data[test]'=>"TEST",
'data[wht]' => 'Whatever'
],
]
// It's working this way...
I hope I am clear enough if u need more info feel free to ask. Thanks in advance.
I see a couple of issues. First is the fact that your $postFields array does not appear to be properly formatted, and second you wrap your $postFields array inside another array.
$options = [
'debug' => true,
'form_params' => [
'test' => 15,
'id' => 43252435243654352,
'name' => 'this is a random name',
],
'on_stats' => $someCallableItem,
];
$response = $client->post('api', $options);

Categories