node js post file to php api on remote server - file

I have to post a file to a php api url on a remote server.
I am creating a http.request and then trying to write file data to this request by piping it from filestream as below :
var req = http.request( options, function(response) {
response.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
var fs = require('fs');
var boundaryKey = "5JtsIWpXzRoEIdPqkQiSWv4Tl8Bmq";//Math.random().toString(16); // random string
var filelen = filelen; \\Already calculated before
req.setHeader('Content-Type', 'multipart/form-data;boundary='+boundaryKey);
req.setHeader('Content-Length', filelen);
// the header for the one and only part (need to use CRLF here)
req.write('--' + boundaryKey + '\r\n'
// "name" is the name of the form field
// "filename" is the name of the original file
+ 'Content-Disposition: form-data; name="gamefile"; filename="image.jpg"\r\n'
// use your file's mime type here, if known
+ 'Content-Type: image/jpeg\r\n'
+ 'Content-Transfer-Encoding: binary_file_data\r\n\r\n'
);
var fileStream = fs.createReadStream('/image.jpg');
fileStream.pipe(req, { end: false }); // maybe write directly to the socket here?
fileStream.on('end', function() {
console.log("File Streamed");
// mark the end of the one and only part
req.end('--' + boundaryKey + '--\r\n\r\n');
});
On my php server what I get in files array :
Array
(
[gamefile] => Array
(
[name] => image.jpg
[type] =>
[tmp_name] =>
[error] => 3
[size] => 0
)
)
which means that my file is not reaching the server.
Please suggest if i am doing something wrong or do need to do something else.

Take a look at the Request module. It makes doing REST requests really easy including doing a POST request with a file included. Take a look at the Forms documentation for more information. Basically it's something like:
var request = require('request');
var r = request.post('http://service.com/upload')
var form = r.form()
form.append('my_field', 'my_value')
form.append('my_buffer', new Buffer([1, 2, 3]))
form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png'))
form.append('remote_file', request('http://google.com/doodle.png'))

Related

post xlsx file to flask route with React async/await

I have a project where I was originally using fetch for all of my requests, and my excel upload function was working fine. I recently made a lot of changes to my application because I added user login and authentication, so I am now trying to reconfigure my routes using async await in React. I am getting different errors that I don't know how to solve. Sometimes when I try to submit the uploaded file I get a 400 error for a bad request, usually saying ""The browser (or proxy) sent a request that this server could not understand.\nKeyError: 'project'". Or I am getting an error on the back-end that says "ValueError: Excel file format cannot be determined, you must specify an engine manually."
I am really confused about where the error is actually occurring because it seems to be reaching the Flask route and then failing.
This is my current request on the front-end:
const onSubmit = async (ev) => {
ev.preventDefault();
let formData = new FormData();
formData.append("project", selectedFile);
// let status = 0;
const data = await api.post("/projects", {
body: formData,
});
if (!data.ok) {
console.log(data);
console.log("error");
} else {
console.log(data);
console.log("uploaded");
}
};
and the back-end route:
#projects.route('/projects', methods=['POST'])
#authenticate(token_auth)
def request_load_pickle_sim():
fn = save_file(None, "project", "xlsx")
print(fn)
request.files["project"].save(fn)
result = process_raw_util_xl_file(fn)
claims = result['claims']
print(claims)
utilities = result['utilities']
weights = result['weights']
maxdiff_scores = result['maxdiff_scores']
data = jsonify(claims)
print(data)
mdp = MaxDiffProject()
mdp.config = MutableDict({
'claims': claims,
'utilities': utilities,
'weights': weights,
'maxdiff_scores': maxdiff_scores
})
print(mdp.config)
db.session.add(mdp)
db.session.commit()
mdp = MaxDiffProject().query.first()
project_config = mdp.config
claims = project_config['claims']
print(claims)
data = jsonify(claims)
return data
My debugger is printing the instance of the file, so it seems like the backend is receiving the file? But after that nothing is working. The file is passed to the process_raw_util_xl_file function (which is where the ValueError error is coming from). I can't figure out what the core issue is because I'm getting conflicting errors. I guess I'm just confused because it was all working fine when I had fetch requests.
The function on the backend is breaking here:
def process_raw_util_xl_file(xl_fn):
# df = pd.read_csv(xl_fn.read())
df = pd.read_excel(xl_fn) # read the excel file
print(df)
row = df.shape[0] # df.shape[0] = Number of rows, df.shape[1] = number of columns
index_col = [col for col in df if 'id' in col.lower()][0]
print(index_col)
df.set_index(index_col, inplace=True) # Setting the ID as the index
# weights = df['Weights'] if 'Weights' in df else pd.Series([1]*row) # Creating the weights array if there are weights , otherwise an array of 1's with # of rows
if 'Weights' not in df:
df['Weights'] = 1
weights = df['Weights']
df.drop('Weights', axis=1, inplace=True)
sum = weights.values.sum() # Sum of the weights
# if 'Weights' in df: df = df.drop('Weights', axis=1) # removing weights from df if they are there
rlh_cols = [col for col in df if 'rlh' in col.lower()][:1]
df = df.drop(rlh_cols, axis=1) # removing RLH from df if they are there
max_diff_scores = (e ** df) / (1 + e ** df) * 100 # exp the values of the dataframe with
utils = df
return {
"utilities": utils,
"claims": [col for col in utils],
"maxdiff_scores": max_diff_scores,
"weights": weights
}
You are posting an object as a body paramter to the server which has a content type as application/json whereas according the HTTP protocol, to post form-data the content type must be multipart/form-data. Here's how you are doing it,
let formData = new FormData();
formData.append("project", selectedFile);
// let status = 0;
const data = await api.post("/projects", {
body: formData,
});
According to the docs (at page end) you must post form data like this,
let formData = new FormData();
formData.append("project", selectedFile);
// let status = 0;
const data = await api.post("/projects", formData);
Also you cannot post body and form data at a same time within single request.

Coinbase body format

My goal is to send a Post request by using Coinbase API.
In the documentation (coinbase), it is specified that the body of the request should be added to the prehash string for message signature.
I am wondering what is the format of this body that I have to sent. Possible way I think I could do that :
concatenation of only the values value1+value2+value3
key=value seperated with &
other way ?
The body should just be a stringified JSON added to the timestamp, method, and path, respectively. Here's the example from the docs:
var crypto = require('crypto');
var secret = 'PYPd1Hv4J6/7x...';
var timestamp = Date.now() / 1000;
var requestPath = '/orders';
var body = JSON.stringify({
price: '1.0',
size: '1.0',
side: 'buy',
product_id: 'BTC-USD'
});
var method = 'POST';
// create the prehash string by concatenating required parts
var what = timestamp + method + requestPath + body;
// decode the base64 secret
var key = Buffer(secret, 'base64');
// create a sha256 hmac with the secret
var hmac = crypto.createHmac('sha256', key);
// sign the require message with the hmac
// and finally base64 encode the result
return hmac.update(what).digest('base64');

Asp.net core file upload with Graphql-dotnet

I am trying to upload an image file with graphql-dotnet, but it is never successful.
I am taking file object in my GraphQLController:
var files = this.Request.Form.Files;
var executionOptions = new ExecutionOptions
{
Schema = _schema,
Query = queryToExecute,
Inputs = inputs,
UserContext = files,
OperationName = query.OperationName
};
And here my Mutation:
Field<UserGraphType>(
"uploadUserAvatar",
Description="Kullanıcı resmi yükleme.",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<IntGraphType>> { Name = "Id", Description = "Identity Alanı" }
),
resolve: context => {
var file = context.UserContext.As<IFormCollection>();
var model = userService.UploadAvatar(context.GetArgument<int>("Id"),file);
return true;
}
);
I think it is accepting the only JSON. It is not accepting the request as a file type.
Also I am using React & apollo-client at the client-side. It has an error in the console:
Failed to load http://localhost:5000/graphql: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 500. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I am trying to send the query like this:
const { selectedFile,id } = this.state
this.props.uploadAvatar({
variables: {id},
file:selectedFile
}).then(result => {
console.log(result);
});
What can I do to achieve this?
Failed to load http://localhost:5000/graphql: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8080' is therefore not allowed
access.
This error means you need to enable CORS.
See these docs: https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1
Essentially you need these two things:
services.AddCors();
app.UseCors(builder =>
builder.WithOrigins("http://example.com"));
I would also suggest to look at this Deserializer helper function in the GraphQL.Relay project. It helps your server handle a multipart/form-data request. You can then use the parsed query information and files and pass it to the DocumentExecutor.
https://github.com/graphql-dotnet/relay/blob/master/src/GraphQL.Relay/Http/Deserializer.cs
public static class Deserializer
{
public static async Task<RelayRequest> Deserialize(Stream body, string contentType)
{
RelayRequest queries;
switch (contentType)
{
case "multipart/form-data":
queries = DeserializeFormData(body);
break;
case "application/json":
var stream = new StreamReader(body);
queries = DeserializeJson(await stream.ReadToEndAsync());
break;
default:
throw new ArgumentOutOfRangeException($"Unknown media type: {contentType}. Cannot deserialize the Http request");
}
return queries;
}
private static RelayRequest DeserializeJson(string stringContent)
{
if (stringContent[0] == '[')
return new RelayRequest(
JsonConvert.DeserializeObject<RelayQuery[]>(stringContent),
isBatched: true
);
if (stringContent[0] == '{')
return new RelayRequest() {
JsonConvert.DeserializeObject<RelayQuery>(stringContent)
};
throw new Exception("Unrecognized request json. GraphQL queries requests should be a single object, or an array of objects");
}
private static RelayRequest DeserializeFormData(Stream body)
{
var form = new MultipartFormDataParser(body);
var req = new RelayRequest()
{
Files = form.Files.Select(f => new HttpFile {
ContentDisposition = f.ContentDisposition,
ContentType = f.ContentType,
Data = f.Data,
FileName = f.FileName,
Name = f.Name
})
};
req.Add(new RelayQuery {
Query = form.Parameters.Find(p => p.Name == "query").Data,
Variables = form.Parameters.Find(p => p.Name == "variables").Data.ToInputs(),
});
return req;
}
}

angular/node.js POST for payumoney integration

I'm using angular/node.js stack for payumoney integration.
On the angular side, an order is placed using $http.post to a route endpoint at the server side (node.js) as follows:
$http.post('/placeOrder',order).success(function(data, status, headers, config){
//handle responses on client side
console.log("Successfully POSTED to payment gateway");
window.location = "https://test.payu.in/_payment";
}).error(function(data, status, headers, config) {
console.log("Error in posting");
});
The actual heavy lifting is done on the node.js (server side):
router.post('/placeOrder', function(req, res, next){
hash_data = MERCHANT_KEY+'|'+txnid+'|'+amount+'|'+productinfo+'|'+firstname+'|'+email+'|'+udf1+'|'+udf2+'|'+udf3+'|'+udf4+'|'+udf5+'||||||'+SALT;
var data = querystring.stringify({
'key': MERCHANT_KEY,
'txnid': txnid,
'amount': amount,
'productinfo': productinfo,
'firstname': firstname,
'email': email,
'phone': phone,
'surl': SUCCESS_URL,
'furl': FAILURE_URL,
'curl': FAILURE_URL,
'hash': hash,
'service_provider': SERVICE_PROVIDER
//'salt': SALT
});
//POST options
var POST_OPTIONS = {
hostname: PAYU_BASE_URL,
port: 443,
path: '/_payment',
method: 'POST',
//json: true,
agent: false,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
//'Content-Length': Buffer.byteLength(data)
'Content-Length': data.length
}
};
var resp_status = "";
var req = https.request(POST_OPTIONS, function(response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log("body: " + chunk);
resp_status = 200;
res.json(chunk);
});
response.on('error', function (err) {
console.log("Got error: " + err.message);
resp_status = 500;
return res.send(err);
});
});
req.end(data);
However, this doesn't seem to work as the POST doesnt seem to work using this approach. While debugging on the browser through the network tab, I always see:
Request URL:https://test.payu.in/_payment
Request Method:GET
Status Code:200 OK
Also, the test payment page (https://test.payu.in/_payment) shows:
"Error Reason
One or more mandatory parameters are missing in the transaction request."
Any help would be appreciated!!
How did I implement this..
Use Jquery and create a Form
Use sha512 to create hashcode. (bower install js-sha512)
var hashString = this.merchantKey+'|'+ options.uid +'|'+ options.totalPrice + '|'+'options.uid + '|' +
options.recipient_name + '|'+ options.email +'|||||||||||'+ this.merchantSalt ;
var hash = sha512(hashString);
var key1 = $('<input></input>').attr('type', 'hidden').attr('name', "key").val("merchantKey");
var key2 = $('<input></input>').attr('type', 'hidden').attr('name', "txnid").val(options.uid);
var key3 = $('<input></input>').attr('type', 'hidden').attr('name', "amount").val(options.totalPrice);
var key4 = $('<input></input>').attr('type', 'hidden').attr('name', "productinfo").val(options.uid);
var key5 = $('<input></input>').attr('type', 'hidden').attr('name', "firstname").val(options.recipient_name);
var key6 = $('<input></input>').attr('type', 'hidden').attr('name', "email").val(options.email);
var key7 = $('<input></input>').attr('type', 'hidden').attr('name', "phone").val(options.phone);
var key8 = $('<input></input>').attr('type', 'hidden').attr('name', "surl").val("http://192.168.43.121/payment/success");
var key9 = $('<input></input>').attr('type', 'hidden').attr('name', "furl").val("http://192.168.43.121/payment/error");
var key10 = $('<input></input>').attr('type', 'hidden').attr('name', "hash").val(hash);
var key11 = $('<input></input>').attr('type', 'hidden').attr('name', "service_provider").val("payu_paisa");
var form = $('<form/></form>');
form.attr("id", "payuform");
form.attr("action", this.payumoneyLink );
form.attr("method", "POST");
form.attr("style", "display:none;");
form.append(key1, key2, key3, key4, key5, key6, key7, key8, key9,key10, key11);
$("body").append(form);
// submit form
form.submit();
This is my first answer on StacksOverflow. Hope it helps!
As per the browser network tab which you have mentioned,
Request URL:https://test.payu.in/_payment Request Method:GET Status Code:200 OK
This means PayU is getting called with GET request instead of POST request somehow. PayU accepts data as a POST request only.
Also, the test payment page (https://test.payu.in/_payment) shows: "Error Reason One or more mandatory parameters are missing in the transaction request."
This is due to GET request. I have faced similar situation in my JSF based application wherein I was sending all parameters correctly but as a GET request. Later on when I switched to POST, the error got resolved automatically.
For information about sending POST request from angular, check below link.
https://www.devglan.com/angular/payumoney-integration-angular
NOTE: If input type is hidden, angularjs has some issue connecting model and view. So please take a note of that. txnid and hash I am getting from AJAX get call so I had to bind it in seperate variable from scope.
Angular code is staright forward, just populate the variables.
One more thing to remember as of today, if your account is not active then you need to use test salt/key provided by their customer support:
MID : 4934580
Key : rjQUPktU
Salt : e5iIg1jwi8
Authorization : y8tNAC1Ar0Sd8xAHGjZ817UGto5jt37zLJSX/NHK3ok=
Test Card : 5123456789012346
Expiry : 05/20
CVV : 123

using HTTPRequest setPayload in google app engine

I am trying to do HTTPRequest Post via Google App Engine.
This is what I have so far
URL url = new URL("http://myurl.com/myfile.php");
HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
request.setPayload(########);
HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(request);
Here I need to put some paired values (ie. "email","hi#example.com" etc)
Since setPayload accept byte[] I have no idea how to convert my paired values
into byte.
I have searched other posts but I am very stuck.
EDIT:
I have changed to this but it is still not working
byte[] data = ("EMAIL=bo0#gmail.com&TITLE=evolution&COMMENT=comments&PRICE=5000;").getBytes();
try {
URL url = new URL("http://www.bo.x10.mx/nPost.php");
HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
request.setPayload(data);
HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(request);
This is what I have on php website.
<?php
include "path/conf.php"; //logging into database works
$tb_name = 'Post';
$EMAIL=$_POST['EMAIL'];
$TITLE =$_POST['TITLE'];
$COMMENT =$_POST['COMMENT'];
$PRICE =$_POST['PRICE'];
if(!isset($EMAIL) || !isset($TITLE ) || !isset($PRICE )|| !isset($COMMENT)){
header('HTTP/1.0 412 Precondition Failed', true, 412);
die('Bad data');
}
$sql="INSERT INTO $tb_name(EMAIL, TITLE, COMMENT, PRICE) VALUES ('$EMAIL', '$TITLE ', '$COMMENT ', '$PRICE ')";
$result=mysql_query($sql);
if($result==TRUE){
echo "successfully inserted into table!";}
else{
echo "error in inserting into table!";
header('HTTP/1.0 500 Internal Server Error', true, 500);}
ob_end_flush();
exit();
?>
EDIT2: This is a working code
try{
byte[] data = ("EMAIL=bo0#gmail.com&TITLE=evolution&COMMENT=comments&PRICE=5000").getBytes("UTF-8");
URL url = new URL("http://www.box.com/nost.php");
HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
request.setPayload(data);
HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(request);
}
My database string field is of type UTF-8
You create a String with the request body, and then you get the byte array. For example we have:
URL url = new URL("http://myurl.com/myfile.php");
HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
String body = "email=" + email + "&mpla=" + mpla;
request.setPayload(body.getBytes("UTF-8"));
HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(request);
Hope this helps!

Resources