PHP api sends HTTP_response_code(400) no matter what - php

i wrote a Simple PHP crud api and i'm getting HTTP_response_code(400) no matter what i do.
in the api a file named create.php is responsible for inserting new items in the database it checks if the data it receives from ajax isn't empty and then proceeds to creation if it's empty it sends an HTTP_response_code(400).
but, no matter what i do it always sends the HTTP_response_code(400) even though the data is not empty.
i thought the problem was coming from ajax first but after debugging i found out that ajax in facts gets the proper data from the form and sends it.
here is my create.php file
$db = $database->getConnection();
$consumable = new consumable($db);
//get json
$json = file_get_contents("php://input");
// get posted data
$data = json_decode($json);
// make sure data is not empty
if(
!empty($data->reference) &&
!empty($data->price) &&
!empty($data->description) &&
!empty($data->category_id) &&
!empty($data->quantity)
){
// set consumable property values
$consumable->reference = $data->reference;
$consumable->price = $data->price;
$consumable->description = $data->description;
$consumable->category_id = $data->category_id;
$consumable->quantity = $data->quantity;
$consumable->created = date('Y-m-d H:i:s');
// create the consumable
if($consumable->create()){
// set response code - 201 created
http_response_code(201);
// tell the user
echo json_encode(array("message" => "consumable was created."));
}
// if unable to create the consumable, tell the user
else{
// set response code - 503 service unavailable
http_response_code(503);
// tell the user
echo json_encode(array("message" => "Unable to create consumable."));
}
}
else{
// tell the user data is incomplete
// set response code - 400 bad request
//http_response_code(400);
// tell the user
echo json_encode(array("message" => "Unable to create consumable. Data is incomplete."));
echo json_encode($json);
}
and here is my ajax:
$(document).on('submit', '#create-consumable-form', function(){
alert("submit");
// get form data
var form=$(this).serializeObject();
var form_data=JSON.stringify(form);
console.log('a',form);
console.log(form_data);
// submit form data to api
$.ajax({
url: "http://localhost:3000/consumable/create.php",
type : "POST",
contentType : 'application/json',
data : form_data,
success : function(result) {
// consumable was created, go back to consumables list
showconsumables();
},
error: function(xhr, resp, text) {
// show error to console
console.log(xhr, resp, text);
}
});
return false;
});
after filling out the form and submitting instead of adding the entry to the database and sending a 201 OK it shows me the following error:
jquery.js:2 OPTIONS http://localhost:3000/consumable/create.php 400 (Bad Request)
send # jquery.js:2
ajax # jquery.js:2
(anonymous) # create-consumables.js:87
dispatch # jquery.js:2
v.handle # jquery.js:2
index.html:1 Access to XMLHttpRequest at 'http://localhost:3000/consumable/create.php' from origin 'http://localhost:5500' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
and this is the result of my console.log
a {reference: "BT3000", price: "10", quantity: "5", description: "description", category_id: "3"}
create-consumables.js:85 {"reference":"BT3000","price":"10","quantity":"5","description":"description","category_id":"3"}
the weird thing is when i comment the HTTP_response_code(400) line in my create.php file it works perfectly does anyone have any idea of the cause of this behaviour?

Try to put header() in your create.php file:
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

json_decode returns null if it cannot decode it. It seems it does just that. Maybe you need to url_decode and/or stripslashes the contents before you can decode it. As Ahmed is saying, try to output the $data variable and the output of the json_decode and file_get_contents("php://input"); and you will see the mistake soon enough.
Also be aware that !empty(0) and !empty(false) returns true. So if your variable has a value which is 0 or false then it will return 400 in this case as well. This is not the issue in your example, but might become an issue later.

the problem was caused by the fact that the content-type in my headers was application/json and i was using postman for testing and i had left it to default content-type which is text

Related

Can an XMLHTTPRequest capture form data, and be sent to a PHP cURL request to be posted?

I have a javascript file that captures form input, makes an XMLHTTP POST request with the input, and handles errors. I am currently POSTing to a separate PHP file, as the request requires sensitive API data and needs encoding. From the PHP file, I then send a cURL request to post the form input to the remote URL.
I want to handle errors based on the cURL response, but the XHR errors are different and take precedent over the cURL errors. Are both these requests necessary, or should I only be making either a single XHR or cURL request?
Whether or not both requests are necessary depends on your use case. Since yours sounds as they are indeed necessary, there's absolutely nothing wrong with it.
How you handle server-side errors client-side is also entirely up to you. For example, let's assume this pretty basic XHR handler, which sends some JSON data off to some endpoint and evaluates the response:
var xhr = new XMLHttpRequest();
xhr.open('POST', '/endpoint.php');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = () => {
const status = xhr.status;
const response = JSON.parse(xhr.responseText);
if (status == 200) {
// Everything's fine
console.log(response.data);
} else {
// Some error occured
console.log(status, response.data);
}
};
xhr.send(JSON.stringify({}));
index.html
The above error handling strategy revolves around the HTTP status code received from the server, so we'll need to make sure they're send according to our needs:
/**
* In case an error occurred, send an error response.
*/
function error(string $message) {
// For properly returning a JSON response, see:
// https://stackoverflow.com/a/62834046/3323348
header("Content-type: application/json; charset=utf-8");
http_response_code(400); // HTTP status code - set to whatever code's appropriate
echo json_encode(['data' => $message]);
}
/**
* Send a response denoting a success.
*/
function success(array $data) {
// For properly returning a JSON response, see:
// https://stackoverflow.com/a/62834046/3323348
header("Content-type: application/json; charset=utf-8");
http_response_code(200);
echo json_encode(['data' => $data]);
}
// For proper error checking on JSON data, see:
// https://stackoverflow.com/a/37847337/3323348
$data = json_decode(file_get_contents('php://input'), true);
// Assume some processing happened. That result is either
// a success, or an error.
$success = mt_rand(0,1);
$success ? success($data) : error('An error occured.');
endpoint.php
Regarding cURL, you could easily change the last two lines to e.g.:
if(curl_errno($ch)) { // Assuming $ch is your cURL handle
error('Curl error: '.curl_error($ch));
}
else {
success($data);
}
Or you adapt the error function - if it would accept an array instead of a simple string, you'd be able to return more complex error data to your JavaScript client:
$curl_error = curl_errno($ch);
if ($curl_error) {
error([
'type' => 'cURL',
'no' => curl_errno($ch),
'message' => curl_error($ch)
]);
}
You don't have to stick with status codes for your error handling, though. You could send a JSON object back with one key denoting some successfully collected data, and one denoting an error if an error occurred. Or whatever suits your needs.

Axios and reactjs showing empty posted data at php backend

Iam using the Axio Reactjs code below to post form data to php backend.
when I check record.php files from the chrome browser console and
network. it shows that connection is okay but posted data is empty. it seems like the axios is not sending the data to php backend.
I have tried some solutions here on SO but cannot get it to work. Any work around will be appreciated.
axios({
method:'POST',
url:'http://localhost/mydata/record.php',
rec:{
myParameter1: 'test',
myParameter2: 'test2',
}
}).then(res => {
const data = res.data;
this.setState({ data });
console.log(data);
})
.catch(err => { // log request error
//this.setState({ error: false });
console.error(err);
})
php code
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header("Access-Control-Allow-Headers: X-Requested-With");
//header("Content-Type: application/json");
//check if file_get_contents is enabled
if( ini_get('allow_url_fopen') ) {
echo "enabled";
} else{
echo "not enabled";
}
$data = file_get_contents("php://input");
$request = json_decode($data);
print_r($request);
print_r($data);
?>
In axios docs you can find data tag:
https://github.com/axios/axios#request-config
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
// When no `transformRequest` is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
},
// syntax alternative to send data into the body
// method post
// only the value is sent, not the key
data: 'Country=Brasil&City=Belo Horizonte',
this is what you need, there is no rec property in axios config
Try removing the key rec in your data segment in axios or better still do something along the lines of
{
rec: {
myParameter1: 'test',
myParameter2: 'test2',
}
}

Ionic 4 POST to PHP backend

I'm trying to POST data to a PHP backend and receive back the values and push it into an array. Hence, I created a function to do just that. However, I'm not to change the API on the backend (written in PHP). So I cannot change it to suit my normal methods of using POST.
This is my function
test() {
let data = "method=getThis" + "&db=myDatabase"
this.http.post("API URL", data).subscribe(data => {
this.result = data; // get data in result variable
this.items = JSON.stringify(this.result); // then convert data to json string
// console.log(this.items);
this.allData = JSON.parse(this.items); // parse json data and pass json string
// console.log(this.allData.length); // got result of particular string
this.array = [];
for (var i = 0; i < this.allData.length; i++) {
this.array.push({
data1: this.allData[i].data1,
data2: this.allData[i].data2,
})
}
console.log(this.array[0])
})
}
And this is an example function on the backend
else if($_POST['method']=="getThis"){
global $conn;
mysqli_select_db($conn, $_POST['db']);
$name="";
$result=array();
$r=mysqli_query($conn,"select data1,data2 from table");
while ($rs = mysqli_fetch_array($r,MYSQLI_ASSOC)){
array_push($result,$rs);
}
echo json_encode(array("result"=>$result));
}
So how do I actually get it to post? I'm stuck here. I usually post with JSON and then decode the JSON on the backend. But this time around I'm not developing the backend and not changing it so gotta use the one provided.
Posting using POSTMAN with this
method=getThis&db=myDatabase
works well. Not sending JSON just a text. So how do I actually achieve this in Ionic.
You could try it that way. It works for me:
First import:
import { map } from "rxjs/operators/map";
Your function:
test() {
let data = "method=getThis" + "&db=myDatabase"
this.http.post("API URL", data).pipe(
map(res => res.json())
).subscribe(response => {
//Here your code
// 'response' is json
});
}
Since the data you are sending is in plain text, you will need to add a header mentioning the content type.
import { HttpHeaders } from '#angular/common/http';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'text/html'
})
};
this.http.post("API URL", data, httpOptions).subscribe()
PHP side should be return JSON and told browser content type is application/json, please test your code base on one simple page.
//demo.php
<?php
$data = ['message' => 'Hello world.'];
header("Content-Type: application/json; charset=UTF-8");
//If allow cross domain and not configration in Ngix/Apache
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Accept-Encoding, X-Requested-With, Content-Type, Origin, Accept, Authenticationtoken");
echo json_encode($data);
And please try http access demo.php again.

PHP Server Response - Backbone save not triggering success function

I can't figure out how to create a correct server response after calling Backbone.Model.save().
Consider myModel to be an initialized Backbone Model instance.
myModel.set('foo', '123');
myModel.save().then( function() {
console.log('saved foo');
}, function() {
console.log('error');
} );
The console output is 'error' allways.
The new value for 'foo' is set on the server though and after reloading the app the view representing myModel.get('foo') will show the expected value ('123').
However the promise never evaluates to the success function.
As my server is very simple I am almost certain that I am doing something wrong there - which is this
if( $_SERVER['REQUEST_METHOD'] == "POST" ) {
if( isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']) ) {
if( $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] == 'PUT' ) {
// ... doing stuff to update data on server
http_response_code(200);
echo '';
}
}
}
This is the received response header
Content-Length 0
Content-Type text/html; charset=UTF-8
Server PhpStorm 9.0.1
X-Powered-By PHP/5.6.12
I very much appreciate every hint on what I am missing here.
header() is used to set HTTP-headers,
So add the line of code
header("Content-type: application/json");
$rtn = array("status", "200");
http_response_code(200);
echo json_encode($rtn);

Unwanted "data" wrapper in PHP response to AngularJS query

I am querying some server with AngularJS using $http.get
var onStuff = function(data) {
console.log( "Stuff received: " + angular.toJson(data));
$scope.stuff = data.data;
};
$http.get("https://some.server.net/stuff")
.then(onStuff, onError);
My back end is written in php and returns a properly formatted JSON.
I checked that loading https://some.server.net/stuff in a browser and testing by command line "php stuff.php" . It looks something like (truncated with ... to fit this screen):
[{"id":"1","user_id":"1","name":"Name1"},
{"id":"2","user_id":"1","name":"Name2"},
...
]
Please note this data is "unwrapped" or "just the array"
However, when onStuff() is invoked my array is "wrapped" inside another data object
Here is the console output
Stuff received:
{"data":[{"id":"1","user_id":"1","name":"Name1"},
{"id":"2","user_id":"1","name":"Name2"},...],
"status":200,
"config":{"method":"GET",
"transformRequest":[null],
"transformResponse":[null],
"url":"https://some.server.net/stuff",
"headers":{"Accept":"application/json, text/plain, */*"}},
"statusText":"OK"}
Here is the php stuff
<?
header('content-type: application/json; charset=utf-8');
header("access-control-allow-origin: *");
require_once("stuff.class.php");
$mysqli = new mysqli( "localhost", "user", "password", "database");
$list = Stuff::getList( $mysqli);
echo json_encode( $list);
$mysqli->close();
?>
I have been following a tutorial using github api, the JSON response was available directly in data
I am pretty sure this has to do with HTTP headers, but I hoped content-type would take care of it
What should I do to remove the unwanted "data" wrapper?
Instead of using the generic promise API (which seems to return an object with everything inside), use the success and error methods provided by $http:
var onStuff = function(data) {
console.log( "Stuff received: " + angular.toJson(data));
$scope.stuff = data.data;
};
$http.get("https://some.server.net/stuff")
.success(onStuff).error(onError);
That should gives you the data in the format you expect. The full API is as follow:
$http({method: 'GET', url: '/someUrl'}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
It seems like you expect onStuff to receive only the deserialized JSON data, but that's not what the API does. The object passed to your $http.get(...).then() callback (i.e. onStuff) is a response object with five properties: data, status, headers, config, and statusText--which is exactly what you're seeing in your console output. The data property has the deserialized JSON data, which is why you have to do $scope.stuff = data.data.
If you want onStuff to receive only the deserialized JSON data, you'll have to call it through an intermediary:
$http.get("https://example.com/stuff")
.then( function(response) {
onStuff(response.data);
} );

Categories