I try in postman like this :
I fill just input password. Then I click button update request
The view like this :
This is header :
This is body. I select raw and input data like this :
Then I click button send and it can get the response
But when I try use guzzle 6 like this :
public function testApi()
{
$requestContent = [
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json'
],
'json' => [
'auth' => ['', 'b0a619c152031d6ec735dabd2c6a7bf3f5faaedd'],
'ids' => ["1"]
]
];
try {
$client = new GuzzleHttpClient();
$apiRequest = $client->request('POST', 'https://myshop/api/order', $requestContent);
$response = json_decode($apiRequest->getBody());
dd($response);
} catch (RequestException $re) {
// For handling exception.
}
}
The result is empty
How can I solve this problem?
See in Postman, you correctly specify the field Authorization in the "headers" tab. So it sould be the same when you use Guzzle, put it in the headers:
public function testApi()
{
$requestContent = [
'auth' => ['username', 'password']
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'json' => [
'ids' => ["1"]
]
];
try {
$client = new GuzzleHttpClient;
$apiRequest = $client->request('POST', 'https://myshop/api/order', $requestContent);
$response = json_decode($apiRequest->getBody());
dd($response);
} catch (RequestException $re) {
// For handling exception.
}
}
Related
Using PHP 8.1, Laravel 9, Guzzle 7.4.2.
I have this snippet in a service singleton registered in AppServiceProvider:
$middleware = new AccessTokenGuzzleMiddleware(
client: new Client([
'base_uri' => config('spica.url') . config('spica.space_api.path'),
'verify' => config('spica.verify_ssl'),
]),
cache: $this->app->make('cache')->store(),
tokenUrl: config('spica.url') . 'auth',
clientId: config('spica.space_api.client_id'),
clientSecret: config('spica.space_api.key'),
);
$stack = HandlerStack::create();
$stack->push($middleware);
$spaceClient = new Client([
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'handler' => $stack,
'base_uri' => config('spica.url') . config('spica.space_api.path'),
'verify' => config('spica.verify_ssl'),
]);
I can't test myself, because I have no access to base_uri, and it will only be tested once on development server, but I am curious if the Client inherits anything from the handler stack, and how does it do so if there are multiple middlewares stacked in handler?
Asking because all the examples in Guzzle docs create the final client with just $client = new Client(['handler' => $stack]);.
As you can see I currently repeat base_uri and verify lines.
Would the following work the same?
$middleware = new AccessTokenGuzzleMiddleware(
client: new Client([
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'base_uri' => config('spica.url') . config('spica.space_api.path'),
'verify' => config('spica.verify_ssl'),
]),
cache: $this->app->make('cache')->store(),
tokenUrl: config('spica.url') . 'auth',
clientId: config('spica.space_api.client_id'),
clientSecret: config('spica.space_api.key'),
);
$stack = HandlerStack::create();
$stack->push($middleware);
$spaceClient = new Client(['handler' => $stack]);
For completeness, here's the AccessTokenGuzzleMiddleware class:
<?php
declare(strict_types=1);
namespace App\Support;
use GuzzleHttp\Client;
use Psr\Http\Message\RequestInterface;
use Illuminate\Contracts\Cache\Repository as CacheContract;
/**
* This is a Guzzle middleware (see more at https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html).
* It adds "Authorization: Bearer" header to requests.
*/
class AccessTokenGuzzleMiddleware
{
public function __construct(
private Client $client,
private CacheContract $cache,
private string $tokenUrl,
private string $clientId,
private string $clientSecret
) {
}
public function __invoke(callable $handler)
{
return function (RequestInterface $request, array $options) use ($handler) {
$request = $request->withHeader('Authorization', 'Bearer ' . $this->getAccessToken());
return $handler($request, $options);
};
}
/**
* Defines cache key for access token.
*
* #return string
*/
private function cacheKey(): string
{
return self::class . $this->tokenUrl . $this->clientId . $this->clientSecret;
}
/**
* Get access token from cache or external API.
*
* #return string
*/
private function getAccessToken(): string
{
$cacheKey = $this->cacheKey();
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$tokenData = $this->retrieveClientAccessTokenData();
$this->cache->set(
key: $cacheKey,
value: $tokenData->access_token,
ttl: $tokenData->expires_in - 10
);
return $tokenData->access_token;
}
/**
* This is effectively the client (this service, not on behalf of a user)
* authenticating itself.
*/
private function retrieveClientAccessTokenData(): object
{
$authResponse = $this->client->post($this->tokenUrl, [
'json' => [
'apiKey' => $this->clientSecret,
'client_id' => $this->clientSecret,
],
]);
assertResponseOk($authResponse);
return json_decode($authResponse->getBody()->getContents());
}
}
EDIT 1
Until I get a better answer, I went with the following:
$this->app->singleton(KeycloakService::class, function (Application $app) {
// Log all Guzzle HTTP requests and responses.
// https://github.com/gmponos/guzzle-log-middleware#advanced-initialization
$middleware = new OAuthGuzzleMiddleware(
client: new Client(['verify' => config('keycloak.verify_ssl')]),
cache: $this->app->make('cache')->store(),
tokenUrl: config('keycloak.url') . 'auth/realms/' . config('keycloak.realm') . '/protocol/openid-connect/token',
clientId: config('keycloak.client_id'),
clientSecret: config('keycloak.client_secret'),
);
$stack = HandlerStack::create();
$stack->push($middleware);
$oAuthKeycloakClient = new Client([
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'handler' => $stack,
'base_uri' => config('keycloak.url') . 'auth/admin/realms/' . config('keycloak.realm') . '/',
'verify' => config('keycloak.verify_ssl'),
]);
return new KeycloakService(
client: $oAuthKeycloakClient,
groupHandler: new GroupHandler(),
userHandler: new UserHandler(),
);
});
$this->app->singleton(SpicaService::class, function (Application $app) {
// Time API
$timeClient = new Client([
'base_uri' => config('spica.url') . config('spica.time_api.path'),
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
'Authorization' => config('spica.time_api.auth_token'),
],
'verify' => config('spica.verify_ssl'),
]);
// Space API
$middleware = new AccessTokenGuzzleMiddleware(
client: new Client(['verify' => config('spica.verify_ssl')]),
cache: $this->app->make('cache')->store(),
tokenUrl: config('spica.url') . 'auth',
clientId: config('spica.space_api.client_id'),
clientSecret: config('spica.space_api.key'),
);
$stack = HandlerStack::create();
$stack->push($middleware);
$spaceClient = new Client([
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'handler' => $stack,
'base_uri' => config('spica.url') . config('spica.space_api.path'),
'verify' => config('spica.verify_ssl'),
]);
// Create service
return new SpicaService(
timeClient: $timeClient,
spaceClient: $spaceClient,
employeeHandler: new EmployeeHandler(),
);
});
That's not really how Guzzle works. So there's three concepts in Guzzle that you're trying to mix up, Client, Middleware and Handler which I'll try to explain.
With the Client you build the request and prepare it for the middleware/handler, this is where you define what the request is.
The Handler executes the request, note: when you create a Handlerstack::create() the default handler will be the CurlHandler, you can overwrite the handler here as a parameter
The Middleware is used to modify the request or response after the request is executed. But the request will eventually be passed to the handler to execute it.
What you can do is provide the client as a service in the AppServiceProvider
$this->app->bind(Client::class, function () {
return new Client([
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'base_uri' => config('spica.url') . config('spica.space_api.path'),
'verify' => config('spica.verify_ssl'),
]);
});
That way everywhere the Client is injected in the constructor it will inject that pre-configured client
Edit:
Just realised that would miss the handler in some cases, you could provide it as a singleton and overwrite the client when you've authenticated
Edit with contextual binding:
$this->app->when(KeycloakService::class)
->needs(Client::class)
->give(function () {
return new Client();
});
$this->app->when(KeycloakService::class)
->needs(Client::class)
->give(function () {
return new Client();
});
$this->app->when(AccessTokenGuzzleMiddleware::class)
->needs(Client::class)
->give(function () {
return new Client([
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'base_uri' => config('spica.url') . config('spica.space_api.path'),
'verify' => config('spica.verify_ssl'),
]);
});
I want to pass the id from the controller to route but I'm having trouble with it. I am a newbie at this so I would really appreciate your help!
Controller:
public function fetchVideo(Request $request)
{
$client = new Client();
$input = $request->all();
$headers = [
'Authorization' => 'Bearer '.$input['token'],
'Content-type' => 'application/json',
'Accept' => 'application/json'
];
$params = [
'id' => $input['id'],
'fields' => $input['fields']
];
$response = $client->request ('GET', 'https://api.dailymotion.com/video/{id}', [
'headers' => $headers,
'query' => $params
]);
return json_decode($response->getBody(), true);
}
Route:
Route::post('/video/{id}', 'App\Http\Controllers\dailymotionController#fetchVideo');
public function fetchVideo(Request $request, $id) // <- {id} parameter in the route
{
...
$response = $client->request('GET', "https://api.dailymotion.com/video/{$id}", [...])
...
}
I want to Post data with matrix, color, and source (upload file) to an API with lumen laravel.
here my code
generateMotif(data, token) {
var path = `motif`;
var formData = new FormData();
formData.append("matrix", data.matrix);
formData.append("color", data.color);
formData.append("source", data.file);
return axios.post(`${API_URL}/${path}`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
token: token,
},
params: {
token: token,
},
})
.catch((error) => {
if (error.response) {
return Promise.reject({
message: error.response.data.error,
code: error.response.status
});
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
// The request was made but no response was received
console.log(error.request);
throw error;
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
// console.log(error.response.data);
throw error;
}
});
},
here my API controller code
public function generateMotif(Request $request)
{
$all_ext = implode(',', $this->image_ext);
$this->validate($request, [
'matrix' => 'required',
'color' => 'required',
'source' => 'required|file|mimes:' . $all_ext . '|max:' . $this->max_size,
]);
$model = new UserMotif();
$file = $request->file('source');
$store = Storage::put('public/upload', $file);
dd($store);
$client = new Client;
try {
$response = $client->request('POST', 'http://localhost:9000/motif', [
'multipart' => [
[
'name' => 'matrix',
'contents' => $request->input('matrix'),
'headers' => [ 'Content-Type' => 'multipart/form-data']
],
[
'name' => 'color',
'contents' => $request->input('color'),
'headers' => [ 'Content-Type' => 'multipart/form-data']
],
[
'name' => 'img_file',
// 'contents' => fopen(storage_path('app/' . $store), 'r'),
'contents' => $request->file('source'),
'filename' => $file->getClientOriginalName(),
'headers' => [ 'Content-Type' => 'multipart/form-data']
],
]
]);
$images = $data->getBody()->getContents();
$filePath = '/public/' . $this->getUserDir();
$fileName = time().'.jpeg';
if ($result = Storage::put($filePath . '/' . $fileName, $images)) {
$generated = $model::create([
'name' => $fileName,
'file_path' => $filePath,
'type' => 'motif',
'customer_id' => Auth::id()
]);
return response()->json($generated);
}
} catch (RequestException $e) {
echo $e->getRequest() . "\n";
if ($e->hasResponse()) {
echo $e->getResponse() . "\n";
}
return response($e->getResponse());
}
return response()->json(false);
}
There are no errors in my controller because I'm not found any error or issues when compiling the code. Response in my controller terminal is 200.
The results that I want from this code are an image. Because in this API, it will process an image and give new image response. But when I'm posting an image, the results are scripts like this.
{data: "<script> Sfdump = window.Sfdump || (function (doc)…re><script>Sfdump("sf-dump-1493481357")</script>↵", status: 200, statusText: "OK", headers: {…}, config: {…}, …}
I don't know what's wrong in my code, but you have any suggestion for my code, please help me. Any suggestions will be very helpful to me.
I am trying to get (StatusCode) in response of REST api while its just return field name and error message like this
[{"field":"Email","message":"Email \"ali#ali.ali\" has already been taken."}]
I have added response
'response' => [
'class' => 'yii\web\Response',
'on beforeSend' => function ($event) {
$response = $event->sender;
if ($response->data !== null && Yii::$app->request->get('suppress_response_code')) {
$response->data = [
'success' => $response->isSuccessful,
'data' => $response->data,
];
$response->statusCode = 200;
}
},
],
Try that way, it works for me:
if ("some error checking goes there") {
Yii::$app->response->statusCode = 422;//I preferred that error code
return [
"data" => [
'errors' => [
'fieldname' => "Field Name is invalid",
]
],
];
}
Yii::$app->response->statusCode
You can Add in you action this code, before return your response
i trying to post data as Async with using Guzzle 6(latest ver)
$client = new Client();
$request = $client->postAsync($url, [
'json' => [
'company_name' => 'update Name'
],
]);
but i am not getting any request form Guzzle like post request on terminal
Because it's a promise, you need to put then
and the promise will not called unless you put $promise->wait()
This is a simple post request using postAsync based on your question:
$client = new Client();
$promise = $client->postAsync($url, [
'json' => [
'company_name' => 'update Name'
],
])->then(
function (ResponseInterface $res){
$response = json_decode($res->getBody()->getContents());
return $response;
},
function (RequestException $e) {
$response = [];
$response->data = $e->getMessage();
return $response;
}
);
$response = $promise->wait();
echo json_encode($response);
Have you tried to send the request?
http://guzzle.readthedocs.org/en/latest/index.html?highlight=async
$client = new Client();
$request = new Request('POST', $url, [
"json" => [
'company_name' => 'update Name']
]);
$promise = $client->sendAsync($request)->then(function ($response) {
echo 'I completed! ' . $response->getBody();
});
$promise->wait();
Guzzle 6 has very little practical examples/documentation for developers to refer. I am sharing an example on how to use postAsync and Pool object. This allows concurrent async requests using guzzle 6. ( I was not able to find a direct example and spent 2 days to get working code )
function postInBulk($inputs)
{
$client = new Client([
'base_uri' => 'https://a.b.com'
]);
$headers = [
'Authorization' => 'Bearer token_from_directus_user'
];
$requests = function ($a) use ($client, $headers) {
for ($i = 0; $i < count($a); $i++) {
yield function() use ($client, $headers) {
return $client->postAsync('https://a.com/project/items/collection', [
'headers' => $headers,
'json' => [
"snippet" => "snippet",
"rank" => "1",
"status" => "published"
]
]);
};
}
};
$pool = new Pool($client, $requests($inputs),[
'concurrency' => 5,
'fulfilled' => function (Response $response, $index) {
// this is delivered each successful response
},
'rejected' => function (RequestException $reason, $index) {
// this is delivered each failed request
},
]);
$pool->promise()->wait();
}
most likely you'll need to call wait();
$request->wait();