request body logic with JSON raw data - php

I am trying to send raw JSON data via Postman as required parameter.
My function works fine except that part. When I paste json data into postman it throws:
A non-empty request body is required.
I think there is a little modification needed but I can't find solution.
My code:
public function callApi(BaseRequest $request)
{
$token = $this->getTokenForRequest($request);
$method = $request->getMethod();
$client = new Client(['base_uri' => 'https://api-tst.testing.app/test/']);
$headers = [
'Authorization' => 'Bearer ' . $token,
'Ocp-Apim-Subscription-Key' => '1111112222',
'Content-Type' => 'application/json',
];
$request->getRequestParams();
$res = $client->request($method, $request->getUrl(), [
'headers' => $headers
]);
$res->getStatusCode();
$response = $res->getBody()->getContents();
return $response;
}
and form my BaseRequest:
public function getUrl()
{
return null;
}
public function getMethod()
{
return null;
}
/**
* #return array
*/
public function getRequestParams()
{
$data = [];
return $data;
}
And I am calling it in controller like:
public function testAll(Request $request)
{
$data = (string)$request->getContent();
$data = json_decode($data, true);
$response = $this->container->get('app')->myFunction($data);
dump($response);die;
}

Found it!
$res = $client->request($method, $request->getUrl(), [
'headers' => $headers,
'json' => $request->getRequestParams()
]);

Related

Retrieve data from external API and pass multiple data to my view

In my controller i have this function
public function GetStatusDetails()
{
$response = Http::get('https://exemple.exemple.com/fr/api/<token>/availability/<Id>/getStatusDetails?format=json');
$StatusDetails = json_decode($response->body(), true);
//dd($data);
return view('ControlmAPI.netvigie', [
'StatusDetails' => $StatusDetails
]);
}
public function GetStatus()
{
$response = Http::get('https://exemple.exemple.com/fr/api/<token>/availability/<Id>/getStatus?format=json');
$Status = json_decode($response->body(), true);
//dd($data);
return view('ControlmAPI.netvigie', [
'Status' => $Status
]);
}
Is not the same call api but when i want to use StatusDetails in my blade i can't but Status i can so my question is how to pass multiple data to my blade and use it separately.
the dd of them is DD so in my blade i do {{$Status[0]['status']}} it work but when i want to do for "StatusDetails" it doesn't but if i do only for "StatusDetails" it works but not for both someone have the solution please ?
You can simply pass them as an array
public function GetStatus()
{
$statusResponse = Http::get('https://exemple.exemple.com/fr/api/<token>/availability/<Id>/getStatus?format=json');
$statusDetailsResponse = Http::get('https://exemple.exemple.com/fr/api/<token>/availability/<Id>/getStatusDetails?format=json');
$Status = json_decode($statusResponse->body(), true);
$StatusDetails = json_decode($statusDetailsResponse->body(), true);
return view('ControlmAPI.netvigie', [
'Status' => $Status,
'StatusDetails' => $StatusDetails,
]);
}

Laravel returns 302 error when trying to send POST request to API route from Laravel Controller

A 302 error is returned when I'm trying to post to API Route, only in the second Post, using the function insereTelefone. When I'm using the Postman, it's working properly, so I think the problem is with Route, but I don't know what. I'm a newbie at the Laravel, so I'm learning how to implement things.
Here is the controller who calls the POST API:
class IndexClientes extends Controller
{
public function index()
{
$request = Request::create('/api/clientes', 'GET');
$response = Route::dispatch($request);
$clientes = json_decode($response->getContent(), true);
return view('index', compact('clientes'));
}
public function create()
{
return view('formulariocliente');
}
public function store(Request $request)
{
$nome = $request->nome;
$cpf = $request->cpf;
$email = $request->email;
$numerosTelefone = $request->telefone;
$tiposTelefone = $request->tipoTelefone;
$request = Request::create('/api/clientes', 'POST', array(
"nome" => $nome,
"cpf" => $cpf,
"email" => $email
));
$responseInicial = Route::dispatch($request);
$response = json_decode($responseInicial->getContent(), true);
$status = json_decode($responseInicial->status(), true);
if ($status !== 200) :
echo "ERRO";
die();
endif;
$idCliente = $response['id'];
if (!empty($numerosTelefone)) :
$i = 0;
foreach ($numerosTelefone as $numeroTelefone) :
$tipoTelefone = (int)$tiposTelefone[$i];
$numeroTelefone = (int)$numeroTelefone;
if (!empty($tipoTelefone) && !empty($numeroTelefone)) :
return self::insereTelefone($idCliente, $tipoTelefone, $numeroTelefone);
endif;
$i++;
endforeach;
endif;
}
public function insereTelefone($idCliente, $tipoTelefone, $numTelefone)
{
$array = array(
"cliente_id" => $idCliente,
"telefone_tipo_id" => $tipoTelefone,
"numero" => $tipoTelefone
);
$request = Request::create('api/telefones', 'POST', $array);
$responseInicial = Route::dispatch($request);
$response = json_decode($responseInicial->getContent(), true);
$status = json_decode($responseInicial->status(), true);
return $status;
}
}
TelefonesController.php
public function store(Request $request)
{
$request->validate(
[
'cliente_id' => 'required',
'telefone_tipo_id' => 'required',
'numero' => 'required|max:11'
]
);
}
api.php
Route::apiResource('telefones', \App\Http\Controllers\TelefonesController::class);
A 302 response usually means your request is being redirected by laravel.
If you are expecting a json response, you need to set the Accept: 'application/json' header along with your request just after the line:
$request = Request::create('api/telefones', 'POST', $array );
$request->headers->set('Accept', 'application/json');
the first
Route::dispatch
was redirecting the page, when I'm was trying to run the second Route::dispatch Laravel returns 302, to solve this I'm using the
app()->handle()
in the function insereTelefone to back to handle the request.
public function insereTelefone($idCliente, $tipoTelefone, $numTelefone) {
$array = array(
"cliente_id" => $idCliente,
"telefone_tipo_id" => $tipoTelefone,
"numero" => $numTelefone
);
$request_telefone = Request::create('api/telefones', 'POST', $array );
$responseInicial = app()->handle($request_telefone);
$status = json_decode($responseInicial->status(),true);
return $status;
}

Using OAUTH API with CURL on a PHP page

I'm working with a PHP website where I want to integrate to some 3rd party API.
In particular I am looking at using CURL to interact with their server but this is something I am far from an expert in so hoping the community can help me gain a better understanding of what I am doing.
I am unclear what options such as -X and -d do, also I am unclear how I script this command on my PHP page? (Unfortunately it's tricky searching google for "-d" as this isn't considered part of the search string)
My particular example I am stuck on is requesting an access token, the API documentation provided to me is;
curl -X POST \
-d \ "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=REQUESTED_SCOPES" \
'https://api.example.com/token'
grant_type- client_credentials
client_id- Generated during setup
client_secret - Web apps only, generated during setup
scope Optional - List of comma separated values, see supported scopes
If the request is successful, an access token will be returned in the
response:
{
"access_token":"ACCESS_TOKEN",
"token_type":"Bearer",
"expires_in":3600
"scope":"REQUEST_SCOPES"
}
That is the above guidance, I have completed the pre-requisites so can confirm the client id, secret and required scope are correct.
I have tried both of the following in vein in my PHP script
$tk = curl_init();
curl_setopt($tk, CURLOPT_URL, "https://api.example.com/token");
curl_setopt($tk, CURLOPT_POST, 1);
curl_setopt($tk, CURL_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($tk, CURLOPT_POSTFIELDS, array( 'grant_type=client_credentials&client_id=myownid&client_secret=xyz123&scope=instrument'));
// grab URL and pass it to the browser
$result=curl_exec($tk);
// close cURL resource, and free up system resources
curl_close($tk);
And
$tk = curl_init();
curl_setopt($tk, CURLOPT_URL, "https://api.example.com/token?grant_type=client_credentials&client_id=myownid&client_secret=xyz123&scope=instrument");
curl_setopt($tk, CURLOPT_POST, 1);
curl_setopt($tk, CURL_HTTPHEADER, array('Content-Type: application/json'));
// grab URL and pass it to the browser
$result=curl_exec($tk);
// close cURL resource, and free up system resources
curl_close($tk);
Both of these examples produce the following error;
{"error_description":"grant_type parameter is
missing","error":"invalid_request"}
Any help on this particular issue or even to just understand how I am going wrong to give me some ideas of the correct syntax will be much appreciated!
Thank you all in advanced for your time.
(Please note I've changed to "example.com" for security of the 3rd
party)
Check out below sample code of cURL call in php. You need to change your domain name instead of example.com also put values for POSTFIELDS.
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://api.example.com/oauth/token",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{\"grant_type\":\"client_credentials\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"scope\": \"instrument\"}",
CURLOPT_HTTPHEADER => array(
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
If you'd like to do it OO instead of using cURL, you might like this better. First up require in Guzzle:
composer require guzzlehttp/guzzle
Create an ApiCredentials Object:
<?php
namespace Some\Company;
class ApiCredentials
{
private $clientKey;
private $clientSecret;
private $proxy;
private $baseUrl;
public function __construct(string $clientKey, string $clientSecret, string $proxy = '', string $baseUrl = 'https://api.somewhere.com')
{
$this->clientKey = $clientKey;
$this->clientSecret = $clientSecret;
$this->proxy = $proxy;
$this->baseUrl = $baseUrl;
}
public function getClientKey(): string
{
return $this->clientKey;
}
public function getClientSecret(): string
{
return $this->clientSecret;
}
public function getProxy(): string
{
return $this->proxy;
}
public function getBaseUrl(): string
{
return $this->baseUrl;
}
}
Now create an ApiService class:
<?php
namespace Some\Company;
use DateTime;
use GuzzleHttp\Client;
class ApiService
{
const API_TOKEN_ENDPOINT = '/token';
private $baseUrl;
private $client;
private $accessToken;
private $credentials;
public function __construct(ApiCredentials $credentials)
{
$this->baseUrl = $credentials->getBaseUrl();
$options = $this->initOptions($credentials);
$this->client = new Client($options);
$this->credentials = $credentials;
}
private function initOptions(ApiCredentials $credentials) : array
{
$options = [
'base_uri' => $this->baseUrl,
'verify' => false,
];
if ($credentials->getProxy() !== '') {
$options = \array_merge($options, ['proxy' => [
'https' => $credentials->getProxy(),
]]);
}
return $options;
}
private function hasAccessToken() : bool
{
return $this->accessToken instanceof AccessToken && $this->accessToken->getExpires() > new DateTime();
}
private function getAccessToken() : AccessToken
{
return $this->accessToken;
}
private function getCredentials(): ApiCredentials
{
return $this->credentials;
}
private function refreshAccessToken()
{
$client = $this->getClient();
$response = $client->post(
$this->baseUrl . self::API_TOKEN_ENDPOINT, [
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
],
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => $this->getCredentials()->getClientKey(),
'client_secret' => $this->getCredentials()->getClientSecret(),
'scope' => 'put your scopes in here',
],
]);
$json = $response->getBody()->getContents();
$this->accessToken = new AccessToken($json);
}
private function getClient() : Client
{
return $this->client;
}
private function validateToken()
{
if (!$this->hasAccessToken()) {
$this->refreshAccessToken();
}
}
public function getSomeEndpointData(string $someParam = 'whatever') : string
{
$this->validateToken();
$response = $this->getClient()->get(
$this->baseUrl . '/get/some/data/' . $someParam, [
'headers' => [
'Authorization' => 'Bearer ' . $this->getAccessToken()->getToken(),
],
'query' => [
'additional' => 'this creates stuff like ?additional=whatever',
],
]);
$json = $response->getBody()->getContents();
return $json;
}
}
And an access token class:
<?php
namespace Some\Company;
use DateTime;
class AccessToken
{
private $token;
private $scope;
private $type;
private $expires;
public function __construct(string $tokenJson)
{
$token = \json_decode($tokenJson, true);
$keys = [
'access_token', 'scope', 'token_type', 'expires_in',
];
$this->token = $token['access_token'];
$this->scope = $token['scope'];
$this->type = $token['token_type'];
$date = new DateTime('+' .$token['expires_in'] . ' seconds');
$this->expires = $date;
}
public function getToken(): string
{
return $this->token;
}
public function getScope(): string
{
return $this->scope;
}
public function getType(): string
{
return $this->type;
}
public function getExpires(): DateTime
{
return $this->expires;
}
}
Now, to use this stuff:
<?php
use Some\Company\ApiCredentials;
use Some\Company\ApiService;
$clientKey = 'client key key here';
$clientSecret = 'client secret here';
$proxy = 'tcp://199.199.132.132:80'; // optional
$creds = new ApiCredentials($clientKey, $clientSecret, $proxy);
$apiService = new ApiService($creds);
$results = $apiService->getSomeEndpointData('whatever'); // returns json
It will handle refreshing access tokens etc too.

Laravel phpunit testing get with parameters

I am writing some tests for my controllers but one of my tests doesn't work. It's supossed to search and get the results back to the page. But it's actually redirecting to the home page. Here is my code:
use DatabaseMigrations;
protected $user;
public function setUp()
{
parent::setUp();
$this->seed();
$this->user = factory(User::class)->create(['role_id' => 3]);
}
/** #test */
public function test_manage_search_user()
{
$response = $this->followingRedirects()->actingAs($this->user)->get('/manage/users/search', [
'choices' => 'username',
'search' => $this->user->username,
]);
$response->assertViewIs('manage.users');
$response->assertSuccessful();
$response->assertSee($this->user->email);
}
The URL you should get to make it work look like this:
http://localhost/manage/users/search?choices=username&search=Test
I checked again and it looks like it's not given in the parameters with the get request. How can I fix this?
I had the same issue trying to test GET Requests, you actually can't pass parameter with the $this->get('uri', [header]) but you can by using $this->call, if you check in MakesHttpRequests.php you can see that this->get() is actually using call method.
By adding an array to get method, you are changing the request headers, this is why you are not getting your parameters.
public function get($uri, array $headers = [])
{
$server = $this->transformHeadersToServerVars($headers);
return $this->call('GET', $uri, [], [], [], $server);
}
public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null)
{
$kernel = $this->app->make(HttpKernel::class);
$files = array_merge($files, $this->extractFilesFromDataArray($parameters));
$symfonyRequest = SymfonyRequest::create(
$this->prepareUrlForRequest($uri), $method, $parameters,
$cookies, $files, array_replace($this->serverVariables, $server), $content
);
$response = $kernel->handle(
$request = Request::createFromBase($symfonyRequest)
);
if ($this->followRedirects) {
$response = $this->followRedirects($response);
}
$kernel->terminate($request, $response);
return $this->createTestResponse($response);
}
So if you want to test a GET Request you will have to do this:
$request = $this->call('GET', '/myController', ["test"=>"test"]);
In your controller you should be able to get theses parameters like so:
public function myController(Request $request)
{
$requestContent = $request->all();
$parameter = $requestContent['test'];
}
I'm using Laravel 5.X (more precisely 5.6), you can pass custom parameters using:
$response = $this->json('GET', '/url/endpoint',['params'=>'value']);
You can use the route helper to build a url with query string. in your case i would do something like this. Assuming the route name is manage.users.search
$route = route('manage.users.search', [
'choices'=> 'username',
'search' => $this->user->username,
]);
$response = $this->followingRedirects()
->actingAs($this->user)
->get($route);
In order to send parameters with GET requests.
If you use the route() method then you can pass the data as the second parameter.
$response = $this->get(route('route_name', ['key' => value]));
If you using URL directly, you could use like this
$response = $this->get('url?' . Arr::query(['key' => value]));
Do whatever you want to do with $response.
You could use the request helper to merge in http get parameters as such:
/** #var \Illuminate\Http\Request $request */
$request = request();
$request->merge([
'choices' => 'username',
'search' => 'Test'
]);
This worked for me simply pass the parameter as part of the url as follows:
$response = $this->get('api/endpoint?parameter1='.$this->dynamicParam);
Add a helper function:
if (!function_exists('extend_url_with_query_data')) {
function extend_url_with_query_data(string $url, array $queryData): string
{
if ($queryData == []) {
return $url;
}
$glue = mb_strpos($url, '?') === false ? '?' : '&';
$queryString = http_build_query($queryData);
return "{$url}{$glue}{$queryString}";
}
}
Usage:
$queryData = [
'works' => true,
];
$this->get(
extend_url_with_query_data('/api/v1/example', $queryData)
);
I would do it like this:
$this->actingAs($this->user);
$response = $this->get('/manage/users/search', [
'choices' => 'username',
'search' => $this->user->username,
]);
$response->assertViewIs('manage.users');
$response->assertSuccessful();
$response->assertSee($this->user->email);

PHP Post Data Formatting

As per the Eventbrite API v3 documentation, the preferred way to submit the data is as JSON. I am attempting to update via ExtJS grid simple organizer data. The changes are not being processed.
The solution is in MODX and the updateFromGrid.class.php looks like this:
class UpdateOrganizerFromGridProcessor extends modProcessor {
public function initialize() {
$data = $this->getProperty('data');
if (empty($data)) return $this->modx->lexicon('invalid_data');
$data = $this->modx->fromJSON($data);
if (empty($data)) return $this->modx->lexicon('invalid_data');
$this->id = $data['id'];
$this->params = array ();
// build JSON content for form submission...cooking key names
$this->formData = array (
'organizer.name' => $data['name'],
'organizer.description.html' => $data['description'],
'organizer.logo.id' => $data['logo_id'],
);
$this->formJSON = $this->modx->toJSON($this->formData);
$this->args = array('id' => $this->id, 'params' => $this->params);
return parent::initialize();
}
public function process() {
// call to main class to save changes to the Eventbrite API
$this->mgr_client = new Ebents($this->modx);
$this->output = $this->mgr_client->postData('organizers', $this->args, $this->formJSON);
$response = json_decode(json_encode($this->output), true);
return $this->outputArray($response);
}
}
return 'UpdateOrganizerFromGridProcessor';
The json output from the above is:
{"organizer.name":"Joe Organizer","organizer.description":"Joe is the Uberest Organizer."}
And my post function is:
//send data to Eventbrite
function postData($method, $args, $JSONdata) {
error_log("JSON Payload : " . $JSONdata);
// Get the URI we need.
$uri = $this->build_uri($method, $args);
// Construct the full URL.
$request_url = $this->endpoint . $uri;
// This array is used to authenticate our request.
$options = array(
'http' => array(
'header' => "Content-type: application/json\r\n"
. "Accept: application/json\r\n",
'method' => 'POST',
'content' => $JSONdata,
'header' => "Authorization: Bearer " . $this->token
)
);
// Call the URL and get the data.
error_log("URL: " . $request_url);
error_log("Content: " . $options['http']['content']);
$resp = file_get_contents($request_url, false, stream_context_create($options));
// parse our response
if($resp){
$resp = json_decode( $resp );
if( isset( $resp->error ) && isset($resp->error->error_message) ){
error_log( $resp->error->error_message );
}
}
// Return it as arrays/objects.
return $resp;
}
function build_uri($method, $args) {
// Get variables from the $args.
extract($args);
// Get rid of the args array.
unset($args);
// Create an array of all the vars within this function scope.
// This should be at most 'method', 'id' and 'data'.
$vars = get_defined_vars();
unset($vars['params']);
// Put them together with a slash.
$uri = implode($vars, '/');
if (!empty($params)) {
return $uri ."?". http_build_query($params);
}
return $uri;
}
The post is working however there is no update to the data and the response back is the original data set. What am I missing here?
I figured it out. It was an issue with a missing slash right before the query string. I also removed the JSON data payload and am submitting as form encoded. The final class is below:
class UpdateOrganizerFromGridProcessor extends modProcessor {
public function initialize() {
$data = $this->getProperty('data');
if (empty($data)) return $this->modx->lexicon('invalid_data');
$data = $this->modx->fromJSON($data);
if (empty($data)) return $this->modx->lexicon('invalid_data');
$this->id = $data['id'];
$this->params = array (
'organizer.name' => $data['name'],
'organizer.description.html' => $data['description'],
'organizer.logo.id' => $data['logo_id'],
);
$this->args = array('id' => $this->id, 'data'=> '', 'params' => $this->params);
return parent::initialize();
}
public function process() {
// call to main class to save changes to the Eventbrite API
$this->mgr_client = new Ebents($this->modx);
$this->output = $this->mgr_client->postData('organizers', $this->args);
$response = json_decode(json_encode($this->output), true);
return $this->outputArray($response);
}
}
return 'UpdateOrganizerFromGridProcessor';

Categories