Im developing a shopify public application using Official Shopify Php API Library and Codeigniter4. I registered for Product update Webhook in shopify which send response to one of my codeigniter controller.
The problem is after processing the request I see empty header values as follows:
{"Host":{},"User-Agent":{},"Content-Length":{},"Accept":{},"Accept-Encoding":{},"Content-Type":{},"X-Forwarded-For":{},"X-Forwarded-Proto":{},"X-Shopify-Api-Version":{},"X-Shopify-Hmac-Sha256":{},"X-Shopify-Product-Id":{},"X-Shopify-Shop-Domain":{},"X-Shopify-Topic":{},"X-Shopify-Webhook-Id":{}}
the controller is:
class ProdHook Extends Controller{
public function index(){
$headers = $this->request->headers();
print_r($headers);
}
}
The $this->trequest->headers() is returns empty header values. But when I replace this with native php function getallheaders(), I can get all header values without any issues. The output of getallheaders() is :
{"Host":"e703.ngrok.io","User-Agent":"Faraday v1.8.0","Content-Length":"2132","Accept":"/","Accept-Encoding":"gzip;q=1.0,deflate;q=0.6,identity;q=0.3","Content-Type":"application/json","X-Forwarded-For":"34.xx.xxx.11","X-Forwarded-Proto":"https","X-Shopify-Api-Version":"2021-10","X-Shopify-Hmac-Sha256":"xxxxxxxx","X-Shopify-Product-Id":"78803xxxxxx922","X-Shopify-Shop-Domain":"xxxxx.myshopify.com","X-Shopify-Topic":"products/update","X-Shopify-Webhook-Id":"uasdhxxxxx-b30c-fdc6e6865609"}
I don't how to fix this issue. I want to stick with native codeigniter4. Thanks in advance.
Retrieving Headers
You can get access to any header that was sent with the request with
the headers() method, which returns an array of all headers, with
the key as the name of the header, and the value is an instance of
CodeIgniter\HTTP\Header:
$headers = $this->request->headers();
array_walk($headers, function(&$value, $key) {
$value = $value->getValue();
});
print_r($headers);
Related
I want to send a request with or without 'Token' as a header.
If request has 'Token' as a header: if the user already has that item, it will return the item with the proper item_id of a specific user (based on its token), otherwise it will return null.
If request doesn't have 'Token' as a header: it will return the item with that item_id
I'm working with Zend Framework and in ItemResource I have this method:
public function fetch($id)
{
}
How can I check if my request has Token as a header or not and implement both cases inside fetch()?
Using Laminas API Tools it depends on wether you 're using a RPC or a REST resource. I will explain which tools the Laminas API Tools give you to evaluate the received header data.
You don 't have to reinvent the wheel, because Laminas API Tools has the received headers already at hand, when you 're in your fetch method.
Representational State Transfer (REST)
Rest resources normally extend the \Laminas\ApiTools\Rest\AbstractResourceListener class. This class listens for \Laminas\ApiTools\Rest\ResourceEvent. Fortunately, this event provides you with a request object that also contains the received header data.
<?php
declare(strict_types=1);
namespace Marcel\V1\Rest\Example;
use Laminas\ApiTools\Rest\AbstractResourceListener;
class ExampleResource extends AbstractResourceListener
{
public function fetch($id)
{
// requesting for an authorization header
$token = $this->getEvent()->getRequest()->getHeader('Authorization', null);
if ($token === null) {
// header was not received
}
}
}
As you can see the ResourceEvent returns a \Laminas\Http\Request instance when calling getRequest(). The request instance already contains all request headers you 've received. Just call getHeader with the given name and as second parameter a default value, which should be returned, when the header was not set. If there is no http_token header, you 'll get null as a result.
Remote Procedure Calls (RPC)
Since RPC requests are handled with a MVC controller class, you can get the request as easy as in a rest resource. Controller classes extend from \Laminas\Mvc\Controller\AbstractActionController, which already contains a request instance.
<?php
declare(strict_types=1);
namespace Marcel\V1\Rpc\Example;
use Laminas\Mvc\Controller\AbstractActionController;
class ExampleController extends AbstractActionController
{
public function exampleAction()
{
$token = $this->getRequest()->getHeader('Authorization', null);
if ($token === null) {
// token was not set
}
}
}
As you can see getting header data in rpc requests is as easy as in resource listeners. The procedure is the same because a request instance is also used here.
Conclusion
There is absolutely no need for coding things, that are already there. Just get the request instance from the event or the abstract controller and retrieve the header you want. Always keep in mind, that there are security aspects like CRLF injections, when dealing with raw data. The Laminas framework handles all this for you already.
Additionally you can check for all received headers by calling ->getHeaders() instead of ->getHeader($name, $default). You 'll get a \Laminas\Http\Header instance with all received headers.
You can get all HTTP header values by getallheaders() or just get the specific value by $_SERVER['HTTP_XXX'], in your case, replace XXX with Token, $_SERVER['HTTP_Token'].
Manual: https://www.php.net/manual/en/reserved.variables.server.php
public function fetch($id)
{
$token = $_SERVER['HTTP_Token'];
// do your busniess code
}
I want to add a header, to request headers, dynamically on the server side.
I am using slim 2 framework which supports middleware.
Here is my usecase:
Client initiates request to url "https://somedomain.com/login" with some Request Headers.
I have middleware say authenticate. Which should add say "UserAddress" to Request Header.
My callback function login() is called. And I need to access "UserAddress" from header.
So following is the code for this route:
function login() {
//Login related stuff
$allHeaders = apache_request_headers();
//Perform some operation on UserAdress from $allHeaders
}
function authenticate(\Slim\Route $route) {
//Perform authentication here
//I am using SUPERFICIAL method `set_apache_request_headers` as reference.
//Here I need to know how I can add new header to REQUEST HEADER
set_apache_request_headers('UserAdress', 'New York');
//Here is what I tried, which did not work when I called apache_request_headers()
//$_SERVER["UserAdress"] = "New York";
}
$app = getSlimInstance();
$app->post('/login', 'authenticate', login);
I tried using $_SERVER, but when I call apache_request_headers(), my header does not show up.
Side Note:
I am using "UserAddress" as my header for reference purpose. Actually I am using different name.
Also I know you guys will say pass that via request body. But due to legacy code I need this in request header.
I just need to know how can I modify the Request Header
I am developing an API using CakePHP 3 framework. Now I'm sending a GET request from POSTMAN client. User will pass an API key in the header.
I wanna fetch this header in my controller function.
This is how my controller looks like
namespace Api\Controller;
use Cake\Auth\DefaultPasswordHasher;
use Api\Controller\AppController;
use Cake\Cache\Cache;
use Cake\Http\ServerRequest;
class ApiController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function myinfo()
{
if($this->request->is('get')) {
$key = $this->request->getHeaderLine('Authorization');
$this->set('key', $key);
}
$this->set('_serialize', ['key']);
}
}
The error that I'm getting is: HeaderLine is not a function
I also tried some more options:
$acceptHeader = $this->request->getHeader('Authorization');
but this also threw similar error. Header is not a function.
Reference: Link
CakePHP version: 3.3.5
As #ndm said in the OP comments, the last example in the linked doc should solve your problem. You are using a version prior to 3.4 so you have to use:
// Prior to 3.4.0
$key = $this->request->header('Authorization');
Refere document for Reading HTTP Header
Here mentioned that "Allows you to access any of the HTTP_* headers that were used for the request". Means it reads only http headers like
HTTP Request
Host
Connection
Upgrade-Insecure-Requests
User-Agent
Accept
Accept-Encoding
Accept-Language
Also mentioned that "While some apache installs don’t make the Authorization header accessible, CakePHP will make it available through apache specific methods as required."
So For Solution
They all have different obscure settings you can tweak to overrule this behaviour, but you'll need to determine exactly which module is to blame.
You can work around this issue by passing the header directly to PHP via the env:
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
Also Refere: Zend Server Windows - Authorization header is not passed to PHP script
Recently I tried to test my REST API's using PHPUnit.
I am facing problem to send http authorization header for my test case.
Every time I do that I get an 403 response instead of 200
Here is my code :
<?php
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Http\Request;
use Zend\Http\Headers;
use Zend\Http\Response;
class TrialTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
$this->setApplicationConfig(
include 'config/application.config.php'
);
parent::setUp();
}
public function testAction()
{
$this->request = new Request();
$this->getRequest()->setMethod('GET');
//$headers = new \Zend\Http\Headers;
//$header = $headers->addHeader($headers->fromString('Authorization:Bearer test'));
$this->getRequest()->sendHeaders('Authorization:Bearer test');
//var_dump($headers);
//$this->getRequest()->setHeaders($header);
$this->dispatch('/campaign');
$this->assertResponseStatusCode(200);
}
}
Kindly help !! where am I going wrong ?
Try setting your headers like this:
$headers = new \Zend\Http\Headers;
$headers->addHeaderLine('Authorization', 'Bearer test');
$this->request->setHeaders($headers);
And you have to make sure that test a valid OAuth token otherwise it will never work. I am not so sure if a 4 character token will ever validate correctly...
UPDATE
I think there is a general problem with your test design. You only set the request object in the controller instance, but the service taking care of authentication has no access to this request object and thus it will not authorize the request correctly.
If you write a controller test in which you test the route '/campaign' you should only test the controller functionality and set mocks for all dependencies. I think the main problem starts in your setUp method. To test this controller you should not load your whole application.config.php. You should set an MvcEvent instance and attach all you need to this event (the correct Router instance, etc) and then dispatch the controller.
Check a proper example of such a ZF2 controller test here.
Testing your OAuth module should happen in an independent test.
In my CakePHP app I return JSON and exit for certain requests. An example of this would be trying to access the API for a login as a GET request:
header('Content-Type: application/json');
echo json_encode(array('message'=>'GET request not allowed!'));
exit;
However I am having to prefix the echo with the content type in order for it to be sent as JSON. Otherwise my code at the other end interprets it different.
Any ideas on how to get around this? Or at least improve it.
Update: Cake version 2.3.0
You can leverage the new 2.x response object:
public function youraction() {
// no view to render
$this->autoRender = false;
$this->response->type('json');
$json = json_encode(array('message'=>'GET request not allowed!'));
$this->response->body($json);
}
See http://book.cakephp.org/2.0/en/controllers/request-response.html#cakeresponse
Also you could use the powerful rest features and RequestHandlerComponent to achieve this automatically as documented: http://book.cakephp.org/2.0/en/views/json-and-xml-views.html
You just need to allow the extension json and call your action as /controller/action.json.
Then cake will automatically use the JsonView and you can just pass your array in. It will be made to JSON and a valid response by the view class.
Both ways are cleaner than your "exit" solution - try to unit-test code that contains die()/exit(). This will end miserably. So better never use it in your code in the first place.