I've built a backend that inserts and reads contents from a database with PHP and MySQLi. I've setup the PHP as REST API endpoints and can GET and POST correctly using Postman. I'm trying to create a client side endpoint to send the API requests but can't figure out how to correctly integrate the code from Postman into the client side.
This is the server side endpoint to GET from the database:
<?php
include_once('config.php');
$task = isset($_GET['task']) ? mysqli_real_escape_string($conn, $_GET['task']) : "";
$sql = "SELECT * FROM `my_to_do_db`.`my_to_do_tb` WHERE task='{$task}';";
$get_data_query = mysqli_query($conn, $sql) or die(mysqli_error($conn));
if(mysqli_num_rows($get_data_query)!=0){
$result = array();
while($r = mysqli_fetch_array($get_data_query)){
extract($r);
$result[] = array("Task" => $task, "Date" => $date, 'Priority' => $priority);
}
$json = array("status" => 1, "info" => $result);
}
else{
$json = array("status" => 0, "error" => "To-Do not found!");
}
#mysqli_close($conn);
// Set Content-type to JSON
header('Content-type: application/json');
echo json_encode($json);
It works correctly when I GET with Postman. Here is the code Postman exports when I click "code" (Javascript fetch):
var urlencoded = new URLSearchParams();
var requestOptions = {
method: 'GET',
body: urlencoded,
redirect: 'follow'
};
fetch("http://localhost/endPointTest/info.php?task=Build API CRUD app", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
Here's how I've been trying to integrate it into a client side page:
<!DOCTYPE html>
<html>
<head>
<title>Get Info API client side</title>
</head>
<body>
<h3 class="displayData"></h3>
<button type="submit" id="getData">Submit</button>
<script type="text/javascript">
const displayData = document.querySelector('.displayData')
document.getElementById('getData').addEventListener
('click', getData);
var urlencoded = new URLSearchParams();
var requestOptions = {
method: 'GET',
body: urlencoded,
redirect: 'follow'
};
function getData(){
fetch("http://localhost/endPointTest/info.php?task=Do some stuff", requestOptions)
.then(response => {
return response.json()
})
.then(data => {
console.log(data)
displayData.innerHTML = data
})
}
</script>
</body>
</html>
My question is basically: how can I integrate the code from Postman into real world pages?
It turns out the problem was that Postman is for testing and doesn't work like a browser. CORS isn't an issue with Postman but it is for a browser, after I added the needed header to the PHP page the API was able to connect.
Related
I have an application in PHP, which receives data from the client that is in another domain. Data arrives from the fetch API via the POST method, but PHP doesn't send a response to the fetch API.
foo.com/endpoint.php:
<?php
include_once('database/connection.php');
header('Content-Type: application/json');
$ip = $_POST["ip"];
$city = $_POST["city"];
$state = $_POST["state"];
$country = $_POST["country"];
$category = $_POST["category"];
// Checking if the req ip already have registered other req
$ip_query = "SELECT * FROM registers WHERE `ip` = '$ip'";
$ip_result = mysqli_query($conn, $ip_query);
$ip_check = mysqli_fetch_assoc($ip_result);
if (!$ip_check) {
// registering data after validation
$new_query = "INSERT INTO `registers` (`ip`, `city`, `state`, `country`, `category`, `created`) VALUES ('$ip', '$city', '$state', '$country', '$category', '2022-07-21 00:00:01')";
$new_create = mysqli_query($conn, $new_query);
$result = array(
'ok' => true,
'status' => 200
);
// sending response
http_response_code(200);
echo json_encode($result);
} else {
// sending response if already registered
http_response_code(503);
}
Client side fetch code:
fetch(this.url, {
method: 'POST',
mode: 'no-cors',
body: this.getFormValues(),
headers: {
"Accept": "application/json",
'Content-Type': 'application/json'
}
})
.then(resp => resp.json())
.then(data => {
console.log(data)
if (data.ok) {
this.Metrics.setSent()
} else {
throw Error(r.statusText)
}
})
.then(() => {
this.Metrics.setSent()
this.Metrics.dismiss()
})
.catch(erro => {
console.log("Erro: ",erro)
this.Metrics.dismiss()
});
It's all right about storing data, my problem is just sending the response :(
PHP does not parse the POST body into JSON automatically. To fix this, you have to add json_decode() to your code like this:
$ip = json_decode($_POST, true)["ip"];
I hadn't noticed that when making a request using the "no-cors" mode i'd get an opaque response.
I just added the code below to the beginning of my PHP file, changed the fetch mode to "cors" and everything worked as it was supposed to work:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization');
header("Access-Control-Allow-Credentials: true");
$method = $_SERVER['REQUEST_METHOD'];
if ($method == "OPTIONS") {
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method,Access-Control-Request-Headers, Authorization") ;
header("HTTP/1.1 200 OK");
die();
}
I made the adaptations suggested by #brrrrrrr and updated the queries to prevent SQL injections.
Thx everyone.
I'm trying to figure out why my code successfully posts the file name, id and email to the db with no problems via Postman but when I try it on the browser with my code - it fails and gives this error in the logs: ErrorException: Trying to get property 'id' of non-object in file.
In Postman, here are my steps:
Successfully authenticate using login endpoint
Successfully post file name, id and email to the db with a separate endpoint
I can't see what I'm doing wrong with fetch(). I'm doing it exactly the same way as my post request in Postman following the same steps in the browser. The only difference's that I'm using an access token and headers to identify who the user is even though in Postman I'm NOT using an access token and headers but it's working fine, oddly.
If I remove the access token and header, then I still get the aforementioned error in the logs, oddly again.
Inside the dd($id, $email); - it's indeed returning the correct information when making the post request so we can rule out these variables aren't the issue.
What am I doing wrong and how can I rectify this?
frontend React.js code:
import React, {Component} from 'react';
import Cookies from 'js-cookie';
class Upload extends Component {
constructor(props) {
super(props);
this.state = {
selectedFile: null,
id: null,
email: '',
fData: ''
};
this.onFormSubmit = this.onFormSubmit.bind(this);
this.onChange = this.onChange.bind(this);
this.fileUpload = this.fileUpload.bind(this);
}
componentDidMount() {
this.getId();
this.getEmail();
}
onFormSubmit(e) {
e.preventDefault();
this.fileUpload(this.state.selectedFile);
}
onChange(e) {
this.setState({ selectedFile: e.target.files[0] });
}
fileUpload(file) {
const formData = new FormData();
const accessToken = Cookies.get("access_token").slice(13,-8);
console.log(accessToken);
formData.append('file', file);
const headers = {
'Contant-Type' : 'application/json',
'Authorization' : 'Bearer ' + accessToken
}
fetch('http://my/api/endpoint/url', {
method: 'POST',
body: formData,
headers: headers
})
.then(response => console.log(response))
.catch(error => { console.error(error) });
}
render() {
return (
<form encType='multipart/form-data' id="login-form" className="form" onSubmit={this.onFormSubmit}>
<input type="file" name="file" onChange={this.onChange}/>
<button type="submit">Upload</button>
</form>
);
}
}
export default Upload;
backend Laravel controller code:
public function store(Request $request) {
$filePath = $request->file('file')->getClientOriginalName();
$id = $request->user()->id;
$email = $request->user()->email;
// dd($id, $email);
$data = [
'file_path' => $filePath,
'user_id' => $id,
'email' => $email
];
DB::table('db.photos')->insert($data);
echo "Record inserted successfully.<br/>";
}
I have an existing PHP code that is doing a curl request to a 3rd-party PHP server.
The 3rd-party server returns a GZIP string.
In PHP, I can use gzdecode to decode the gzip string.
How can I do it in NodeJS/Javascript? I tried using decompress-response with no avail.
Also tried using got instead of request, enabled auto-decompress, also doesn't work.
Edit: Also tried zlib and pako, also doesn't work.
Sample Code [ PHP ]
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $params,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 3000000,
CURLOPT_ENCODING => '',
CURLOPT_CUSTOMREQUEST => "GET",
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo false;
} else {
$response = gzdecode($response);
echo $response;
}
This is the solution that works for me.
I used got instead of axios because I can't get it to work there.
I set my request options:
const requestOptions = {
encoding: null, // this is important
headers: {
"Accept-Encoding": "gzip",
}
...
};
Don't forget that encoding: null line, because without that, the response will be automatically converted to a string. ( We need a buffer to make this work )
Then I created a function like this to handle my request:
const zlib = require("zlib");
async function performRequest(url, options) {
try {
const response = await got(url, options);
if (response.headers["content-encoding"] === "gzip") {
const body = response.body;
try {
const dezziped = zlib.gunzipSync(response.body);
response.body = JSON.parse(dezziped.toString());
} catch (error) {
response.body = body;
}
}
return response.body;
} catch (error) {
return error;
}
}
Note: This is a synchronous operation, you can use gunzip instead if you want to do the async way.
The guy responsible for API requests is gone for the week, so nothing can be done on server side.
fetch("https://url.com/api/login/", {
method: "post",
headers: {
// 'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: JSON.stringify({
username: "test#mail.com",
password: "123"
})
}).then(function (response) {
return response.json();
}).then(function (myJson) {
console.log(myJson);
});
It works on Postman, but as I heard, Postman doesn't follow the same security as browsers, therefore this isn't an issue for Postman. But I doubt this is the case, as the authors php-solution works fine.
This is an example of php-solution that works (He wrote it):
function login($username, $password) {
$curl = curl_init(); curl_setopt_array($curl, array(
CURLOPT_URL => "https://url.com/api/login/",
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_MAXREDIRS => 10,
CURLOPT_POSTFIELDS => "username=".$username."&password=".$password,
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache",
"content-type: application/x-www-form-urlencoded"),
));
$response = curl_exec($curl);
curl_close($curl);
$authdata = json_decode($response);
if ($authdata -> success) {
//success
return true;
} else {
//fail
return false;
}
}
What's missing in my code? How can I make it work like his php solution. (Have no experience in php).
Any help is much appreciated.
EDIT:
What worked on Postman:
Raw json format in Body.
Adding values as Key and Value in x-www-form-urlencoded
To solve this error you can do 3 things:
Add your origin server side.
Run your javascript on the same domain.
Check this answer for disabling same origin policy in chrome. This will allow you to test your code until the guy responsible for de API returns.
I don't seem to find any missed end input. This is the error that I recieve:
Uncaught (in promise) SyntaxError: Unexpected end of input
at fetch.then.response (InventoryOnHand.js:33)
Below is my code: I have a value for the url.
fetch(url + "GetItemMasterList", { 'mode': 'no-cors' })
.then(response => response.json())
.then(function (data) {
console.log(data)
});
This is a well known problem with CORS policy. To overcome this problem you need access rights to the server side API. In particular, you have to add a line in the header of php or another server endpoint:
<?php
header('Access-Control-Allow-Origin: *');
//or
header('Access-Control-Allow-Origin: http://example.com');
// Reading JSON POST using PHP
$json = file_get_contents('php://input');
$jsonObj = json_decode($json);
// Use $jsonObj
print_r($jsonObj->message);
...
// End php
?>
Also, make sure NOT to have in the header of your server endpoint:
header("Access-Control-Allow-Credentials" : true);
Model of working fetch code with POST request is:
const data = {
optPost: 'myAPI',
message: 'We make a research of fetch'
};
const endpoint = 'http://example.com/php/phpGetPost.php';
fetch(endpoint, {
method: 'POST',
body: JSON.stringify(data)
})
.then((resp) => resp.json())
.then(function(response) {
console.info('fetch()', response);
return response;
});