I'm having a problem when I receive my JSON in a React Native app.
I'm doing a simple fetch request to get it
getApplicationList()
{
return fetch('http://192.168.X.X/index.php')
.then((response) => response.json())
.then((responseJson) => {
return responseJson;
})
.catch((error) => {
return 'There has been a problem with your fetch operation: ' + error.message;
});
}
but when I make a console.error(getApplicationsList()); in the render function, I'm getting a very bizarre log on the android emulator:
console.error: {"_40":"0","_65":"0","_55":"null","_72":"null"}
In my PHP file, I'm returning a JSON encoded response, and when I type my URL (with 'localhost') in the navigator I can get it.
When I launch the app from the emulator, I change the localhost adress with my computer IP, and when I type this URL in the browser, I'm getting a Cannot GET /index.php response. I don't know if it's alright, and I'm feeling a little lost between all of this.
Many thanks for your help
I found a solution using a real android device:
I followed this to make a new port for my device: Connect an Android Device To a Web Service on Local Host (the answer using the Chrome navigator settings)
and then I made my HTTP request in the app using the IP address of my computer with the port I typed in the previous link. And it worked.
Related
I'm posting this on my way home, so forgive the lack of code but I'll try to be as detailed as possible and add code when I can tonight. So essentially I have a react native app using redux and axios. A brief review (code to follow) may explain that I'm doing something wrong.
Serviceapi.js
Creates and exports basic axios with base url.
const ServiceApi = axios.create({
baseURL: BASE_URL,
responseType: 'json'
});
AuthReducer.js
On login sets Authorization header manually using the post method. This works on both android and ios the login is returned and I use the authorization header.
return {
type: PERFORM_LOGIN,
payload: {
user: {
name: username
},
request: {
url: '/login',
method: 'post',
headers: {
'Authorization': 'Basic ' + basicAuth
}
}
}
On login, I return the following redux-axios action, you can see that I set the header: Authorization manually, this works great.
// On login success, set the authInterceptor responsible for adding headers
authInterceptor = ServiceApi.interceptors.request.use((config) => {
console.log(`Attaching Authorization to header ${basicAuth}`);
config.headers.common.Authorization = basicAuth;
return config;
}, (error) => {
Promise.reject(error);
});
On logout I clear the interceptor. I chose to add and remove on login and logout instead of always having it there just because. This could be a problem but it was fine for Android
// Clear the auth interceptor
ServiceApi.interceptors.request.eject(authInterceptor);
Again this is all working great on Android. And it looks to be working on ios. When I debug the interceptor it's getting called and setting the header.
But I get back a 403 on ios. After looking at the request in more detail, there is a big difference between the android header in the request and the ios header in the request. The rest of the request object is the same, only the _header object is different between ios and android.
Android Request
_headers:
accept: "application/json, text/plain, */*"
authorization: "Basic <correct base64 value>"
content-type: "application/json;charset=utf-8"
__proto__: Object
IOS Request
_headers:
accept: (...)
authorization: (...)
content-type: (...)
get accept: ƒ ()
set accept: ƒ ()
get authorization: ƒ ()
set authorization: ƒ ()
get content-type: ƒ ()
set content-type: ƒ ()
__proto__: Object
With the differences, setting a breakpoint at looking at the console for error.request._headers.authorization; I get the same "Basic: " contents as the Android header contains.
index.php
The backend service is a php file that does a $_SERVER['PHP_AUTH_USER'] which fails a 403 if not set which is what's happening. I don't have access to the php, I was just told this is what it's using.
Again I apologize for not providing code but i will when I get a chance later. Is there something maybe I have to set extra for ios? Or maybe php for ios needs an extra header?
Code to follow.
EDIT Updated with code, hopefully I didn't leave in any of the encoded login info.
EDIT 2 Upon further investigation this looks like it's related to apache/PHP rather than react-native/axios. I threw together an express server that simulated the same checking that the PHP does:
- Look for the Authorization header
- Print it
- Return back 403 or 200 w/ data based on that
When running pointing at http://localhost:3000 using the exact same app on the emulator I get back what I'm expecting. To add to this, when I'm on the emulator, I can't actually login to the live URL (even though I could on the regular device), I get the same 403 error but this time a little earlier.
EDIT 3
To provide some more information from the server, here are the three requests that I've been able to log:
1) This is from the IOS Emulator iPhone8 against a an express server:
accept:"application/json, text/plain, */*"
accept-encoding:"gzip, deflate"
accept-language:"en-us"
authorization:"Basic <base 64 encoding>"
connection:"keep-alive"
content-length:"0"
host:"localhost:3000"
user-agent:"MobileApp/1 CFNetwork/978.0.7 Darwin/18.5.
2) This is from the same emulator to apache/PHP (5.3.3), we can see there is no Authorization header.
Accept: application/json, text/plain, */*
User-Agent: MobileApp/1 CFNetwork/978.0.7 Darwin/18.5.0
Accept-Language: en-us
Accept-Encoding: br, gzip, deflate
Connection: keep-alive
3) This is from Android to apache/PHP (5.3.3):
authorization: Basic <Base 64 encoding>
Host: api.serviceurl.com
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.12.1
Edit 4
So after playing around and googling for some time, it turns out that the issue is with Zend Framework and fastcgi which automatically removes the Authorization header. The weird thing is that it's only doing it from IOS and not from Android, which makes no sense really.
On thing we noticed in the logs, is that it's accepting the Android and Postman as POST but it's logging the IOS requests as GET. I'm not entirely sure what's up with that, but it seems to be another difference. I've updated the task to have zend as a tag. There are a number of SO articles on resolving this with ReWriteMod on apache/zend so I'll give those a go first and see if it fixes the issue.
** Edit 5**
So far we've attempted to follow the SO articles which ask that that that following be added (Authorization header missing in django rest_framework, is apache to blame?):
SetEnvIfNoCase Authorization ^(.*) -e=PHP_HTTP_AUTH
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
which results in the following:
// IOS
_SERVER[PHP_HTTP_AUTH] = <blank>
_SERVER[HTTP_AUTHORIZATION] = <blank>
// Android
_SERVER[PHP_HTTP_AUTH] = Username
_SERVER[HTTP_AUTHORIZATION] = Basic <Base65 encoded>
_SERVER[PHP_HTTP_PW] = Password
So we know that Header Authorization is getting to Apache, but now it's coming through as blank. There are a few other SO answers I'm researching but the search continues...
Edit 6
Resolved(ish)
Turns out it was a trailing slash required on the request for IOS. I was able to find this link https://github.com/square/retrofit/issues/1037 where the the issue was described as:
For those interested: We are using Django as our backend and by default when you do
not provide a trailing slash on the endpoint Django redirects from the non-slash
endpoint to the slash endpoint.
Now, we aren't using Django, but apparently for our configuration of Zend it was
the same issue - Android was able to re-direct without issue, while IOS was not. Another comment on the task states:
OkHttp strips the "Authorization" header when redirected across hosts (connections)
via a 3xx response from the original host.
Which doesn't seem accurate, since Android was using OkHttp and was working fine. It looked like IOS using Darwin had the issue.
EDIT
I forgot something else from my original post, I also had to change my interceptor from the line config.headers.common.Authorization = ... to config.headers.Authorization = ... which for some reason kept the casing. Original way converted Authorization to authorization, while the latter kept it as Authorization. Not sure if this was an issue, but I made it anyhow.
// On login success, set the authInterceptor responsible for adding headers
authInterceptor = ServiceApi.interceptors.request.use((config) => {
console.log(`Attaching Authorization to header ${basicAuth}`);
config.headers.Authorization = basicAuth;
return config;
}, (error) => {
Promise.reject(error);
});
I cannot believe I spent 5 hours debugging and researching to resolve the issue eventually with a trailing slash! Even when I tried the trailing slash I thought it was a futile attempt but it actually resolved my issue. #kendavidson you're a lifesaver!!
basically I want to know how to receive the JSON data from IFTTT when sending a web request to a specific URL on my web server.
I know that IFTTT will send a web request with the JSON data to my .php file which is a public hosted web page (92.123.xxx.xxx:8089/MyPhpFile.php), but how would I know if that web request was actually sent or not because when I try running my PHP script on my web browser it just says nothing ie no POST data was received.
Basic Flow Of IFTTT Setup:
Tell google home (google assistant) to switch TV1 to Xbox.
Run WebHook Applet which sends a web request to the 92.123.xxx.xxx:8089/MyPhpFile.php using method POST, content type application/json and body {“token”:”mseries”,”command”: “{{NumberField}}”, ”test”: “data”}
???? This is where I become confused because I need to Receive JSON data and execute the proper python scripts to send telnet commands to my matrix switcher.
MyPhpFile.php
<?php
$token = "mseries";
# Capture JSON content
$input=json_decode(file_get_contents('php://input'), true);
# Check if correct TOKEN passed or else echo nothing
if($input['token'] != $token) {
echo "nothing";
exit;
}
switch ($input['test']) {
case 'data':
echo print_r($data);
}
?>
I've got a very strange issue.
local hosted PHP Slim App using XAMPP (localhost:4040)
local hosted Angular 4 App using CLI (localhost:4200)
Making API Requests using "Postman" and browser is no problem, everything works fine.
Now I'm integrating the requests into my Angular app using import { Headers, Http } from '#angular/http'; and observables.
const requestUrl = 'http://localhost:4040/register';
const headers = new Headers({
'content-type': 'application/x-www-form-urlencoded'
});
this.http
.get(requestUrl, {headers: headers})
.map(response => response.json())
.subscribe(result => {
console.log(result);
}, error => {
console.log(error);
});
The request always fails with:
Failed to load http://localhost:4040/register: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
But: I am definitely sending these headers!
public static function createJsonResponseWithHeaders($response, $requestedData)
{
// Add origin header
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
$response = $response->withHeader('Access-Control-Allow-Methods', 'GET');
// Add json response and gzip compression header to response and compress content
$response = $response->withHeader('Content-type', 'application/json; charset=utf-8');
$response = $response->withHeader('Content-Encoding', 'gzip');
$requestedData = json_encode($requestedData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
$response->getBody()->write(gzencode($requestedData), 9);
if (!$requestedData || (count($requestedData) === 0)) {
return $response->withStatus(404)->write('Requested data not found or empty! ErrorCode: 011017');
}
return $response;
}
What I already tried for solving:
Run Slim App inside a Docker Container to get a different origin than localhost - same behaviour
Add allow-origin-header right on top of the index.php
header('Access-Control-Allow-Origin: *'); - same behaviour
Your requests are blocked because of CORS not being set up properly. There are other questions that address this, e.g. How to make CORS enabled requests in Angular 2
What you should ideally look at using is a proxy that forwards your requests to the API, the latest Angular CLI comes with support for a dev proxy (see https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md) out of the box. You set it up with a proxy.conf.json that could look like this:
{
"/api": {
"target": "http://localhost:4040",
"secure": false,
"pathRewrite": {"^/api" : ""}
}
}
What this piece of code does is any requests from Angular to a URI matching /api will be forwarded to localhost:4040.
Note that you will also need to figure out how your app will talk to the API server in a non-dev environment. I have been happy with using Nginx to serve Angular files, and act as proxy for the API.
Sorry, my bad. The solution is simple:
The "Cache-control" header in the request seems to be not allowed, although it worked fine when testing the api with Postman.
I removed the header from the request and everything worked well.
I am currently working on an AngularJS project with a server backend written in PHP. The frontend and backend communicate entirely in JSON, however, there is an export scenario where the server's output is not JSON encoded but instead a (text or binary) file.
The web application cannot just redirect the client's browser to a download URL as the server requires custom headers in the HTTP request (i.e. an API key) to serve the file. Therefore, I am using $http in AngularJS to initiate an AJAX request. Here is what happens:
File generation on the server side (using PHP with Slim framework):
$export = $this->model->export_cards($project_key);
$this->app->response()->status(200);
$this->app->response()->header("Content-Type", "text/plain");
$this->app->response()->header("Content-Disposition", "attachment; filename=\"export.txt\"");
$this->app->response()->header("Last-Modified", date("r");
$this->app->response()->header("Cache-Control", "cache, must-revalidate");
$this->app->response()->body($export);
$this->app->stop();
This is what happens on the client side (so far):
$http({
method: "get",
url: "/server/projects/cards/export_cards/" + $scope.key,
headers: {
"X-API-Key": session_service.get("api_key")
}
}).then(
function(response)
{
// Success, data received
var data = response.data; // This variable contains the file contents (might be plain text, or even binary)
// How do I get the browser to offer a file download dialog here?
},
function(response)
{
// Error handling
}
);
I successfully receive the file contents in the AngularJS frontend and store them in a variable data. How do I get the browser to display a file download dialog?
The solution must work in Internet Explorer 10+ and reasonably recent versions of Firefox, Chrome and Safari (only desktop versions).
What is the best way to achieve this?
Thank you for your help and let me know if I need to provide any additional information.
Peter
I'm not sure this is possible.
Could you either:
Supply the API key directly, eg:
location.href = "/server/projects/cards/export_cards/" + $scope.key + '?api_key=' + session_service.get("api_key");
Or, have your API return a temporary, time-expiring URL for the file download, and then use location.href to access this URL.
I personally don't see how this can be done even though these are my servers. But I want to know if my servers can reach external sites--ping a generic website for example--have outgoing communication. That is, I want to use execute a PHP script on one server, connecting to another of my servers, and test if the second server can ping a website, for example. So I know how to use PHP on the server my script is executing from to ping a website with fopensocket. I just don't know how to set this up to test another server's pingability. I mean I have the credentials but the only way is to have my script on each and every server and then reach the script and execute them. That is not what I want. I want to do this from the one/external server and just feed my script the ip/port/uid/pwd of the server I want to test.
An easy API would look something like:
SERVER1:
// get response from server2
$response = file_get_contents('http://www.server2.com/api.php?method=ping&ip=IP&port=PORT&uid=UID&pwd=PWD');
// do json_decode() if response is json string
SERVER2 (api.php):
// respond to API call
if (isset($_GET['method']) && $_GET['method'] == 'ping') {
// get other params and do your ping function
echo $pingresult; // perhaps a json encoded array
exit;
}
There is no security so you could send an API password or do it with OAuth or HMAC