I've read node.js Extracting POST data.
But here's my problem, how to extract POST data with Express when I received a HTTP request looking like this?
POST /messages HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 9
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5
Content-Type: application/xml
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: UTF-8,*;q=0.5
msg=hello
I can't seem to get the msg=hello key-value pair out of the body with Express.
I've tried all of these methods req.header() req.param() req.query() req.body but they seem to be empty.
How to get the body's content?
app.post('/messages', function (req, res) {
req.??
});
Your problem is bodyParser does not handle 'application/xml', I solved this mainly by reading this post: https://groups.google.com/forum/?fromgroups=#!topic/express-js/6zAebaDY6ug
You need to write your own parser, I've published the below with more detail to github:
https://github.com/brandid/express-xmlBodyParser
var utils = require('express/node_modules/connect/lib/utils', fs = require('fs'), xml2js = require('xml2js');
function xmlBodyParser(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
// ignore GET
if ('GET' == req.method || 'HEAD' == req.method) return next();
// check Content-Type
if ('text/xml' != utils.mime(req)) return next();
// flag as parsed
req._body = true;
// parse
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ buf += chunk });
req.on('end', function(){
parser.parseString(buf, function(err, json) {
if (err) {
err.status = 400;
next(err);
} else {
req.body = json;
next();
}
});
});
}
then use it with
app.use (xmlBodyParser);
If you have this in the config:
app.use(express.bodyParser());
And this in your view:
form(name='test',method='post',action='/messages')
input(name='msg')
Then this should work:
app.post('/messages', function (req, res) {
console.log(req.body.msg);
//if it's a parameter then this will work
console.log(req.params.msg)
});
I believe you need to configure express to use the bodyParser middleware.
app.use(express.bodyParser());
See the express documentation.
It says:
For example we can POST some json, and echo the json back using the bodyParser middleware which will parse json request bodies (as well as others), and place the result in req.body
req.body() should now return the expected post body.
I hope this helps!
It's POSSIBLE (not sure what it depends on, but it happened to me once, it might be the bodyParser) that the request body is formatted in such a way that your JSON data is ITSELF being treated as a key in a key-value pair, with a blank corresponding value. What's worked for me in this situation was to extract the JSON object first and then proceed as normal:
var value;
for (var item in req.body)
{
var jObject = JSON.parse(item);
if (jObject.valueYouWant != undefined)
{
value = jObject.valueYouWant;
}
}
This is probably pretty suboptimal, but if nothing else works (I tried for ages trying to find a better way and found none) this might work for you.
You are posting xml as I can see, the answers you got were based on JSON input. If you want the content of your xml displayed, process the raw request :
app.post('/processXml',function (req, res)
{
var thebody = '';
req.on('data' , function(chunk)
{
thebody += chunk;
}).on('end', function()
{
console.log("body:", thebody);
});
});
As an example using curl as your postman:
curl -d '<myxml prop1="white" prop2="red">this is great</myxml>' -H
"Content-type: application/xml" -X POST
http://localhost:3000/processXml
Outputting:
'<myxml prop1="white" prop2="red">this is great</myxml>'
Make sure your body-parser middleware doesn't get in the way: body-parser-xml processes your request object on the fly to a json object, after which you cannot process your raw request anymore. (And you can guess who was stuck several hours after this...)
Related
I'm trying to create an API endpoint that accepts several files (that's why I need POST request, not GET request) and responds with another file. I want browser to present a "save as..." dialog (or just start downloading).
Here's a demo code that doesn't work (Flask):
#app.route('/api', methods=['POST'])
def api():
return send_file('./sample.txt', as_attachment=True)
I can see proper response headers, but nothing happens in the browser:
HTTP/1.0 200 OK
Content-Disposition: attachment; filename=sample.txt
Content-Length: 612
Content-Type: text/plain; charset=utf-8
If I remove methods=['POST'] from endpoint and issue a GET request to it, it works fine, the browser asks if I want to save this file.
What am I doing wrong or it's just how things are (Content-Disposition ignored for POST responses?)
Seems browser shows pop-up for the download, only for the GET requests. But there is a way to show it from client side. Something like this:
document.getElementById('download').addEventListener('click', function () {
var content = document.getElementById('content').value;
var request = new XMLHttpRequest();
request.open('POST', '/api', true);
request.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8');
request.responseType = 'blob';
request.onload = function() {
if(request.status === 200) {
var filename = 'sample.txt';
// The actual download
var blob = new Blob([request.response], { type: 'text/plain' });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// some error handling should be done here...
};
request.send('content=' + content);
});
More details in this question and in this post
I need a way to set the headers of the dart http Request object to application/JSON.
I want to build a Request object to send to my backend API. I set the body to my JSON object, but when it gets sent, it defaults the headers to text/html instead of application/json.
I have tried using the built-in method
http.post(url,dynamic body);
but unfortunately this method places the body in the parameters of the URL and I need it in the actual body of the request.
So instead I built an http Request object, and manually set the URL and body but like I said, it sets the headers to text/html.
I have read the docs for https://pub.dev/documentation/http/latest/http/Request-class.html, but unfortunately, I haven't found a way to set the headers.
postRequest(uri) async {
Uri url = Uri.tryParse("https://ptsv2.com/t/umt4a-1569012506/post");
http.Request request = new http.Request("post", url);
request.body = '{mediaItemID: 04b568fa, uri: https://www.google.com}';
var letsGo = await request.send();
print(letsGo.statusCode);
}
Much thanks for any possible solutions!
Ps. this is my first ask on Stack Overflow so I apologize if I made any errors in posting.
Solved!
postRequest(uri) async {
Uri url = Uri.tryParse("https://ptsv2.com/t/umt4a-1569012506/post");
http.Request request = new http.Request("post", url);
request.headers.clear();
request.headers.addAll({"content-type":"application/json; charset=utf-8"});
request.body = '{mediaItemID: 04b568fa, uri: https://www.google.com}';
var letsGo = await request.send();
print(letsGo.statusCode);
}
I was having some issues with the Request object default setting the encoding.
By manually specifying utf-8, the server I am contacting accepts it.
for the post or get any request you can Add Header like this -
var permAddUrl = 'your requested url';
var bodyParameters = {
'Email': email,
'MobileNo': mobileNumber,
};
await http.post(
requesturl,
headers: { 'Content-Type': 'application/x-www-form-urlencoded',
"Authorization":"$token",
},
body: bodyParameters,).then((response) {
var data = json.encode(response.body);
print(data);
setState(() {
if(response.statusCode == 200){
//var statesList = data['data'];
UtilAction.showSnackBar(context, " Details Submitted Successfully");
}
});
});
I am implememting file upload using Angular 2 with ASP.NET core Web API to handle the request.
My html code looks like :
<input #fileInput type="file"/>
<button (click)="addFile()">Add</button>
and the angular2 code
addFile(): void {
let fi = this.fileInput.nativeElement;
if (fi.files && fi.files[0]) {
let fileToUpload = fi.files[0];
this.documentService
.uploadFile(fileToUpload)
.subscribe(res => {
console.log(res);
});
}
}
and the service looks like
public uploadFile(file: any): Observable<any> {
let input = new FormData();
input.append("file", file, file.name);
let headers = new Headers();
headers.append('Content-Type', 'multipart/form-data');
let options = new RequestOptions({ headers: headers });
return this.http.post(`/api/document/Upload`, input, options);
}
and the controller code
[HttpPost]
public async Task Upload(IFormFile file)
{
if (file == null) throw new Exception("File is null");
if (file.Length == 0) throw new Exception("File is empty");
using (Stream stream = file.OpenReadStream())
{
using (var binaryReader = new BinaryReader(stream))
{
var fileContent = binaryReader.ReadBytes((int)file.Length);
//await this.UploadFile(file.ContentDisposition);
}
}
}
My RequestHeader looks like
POST /shell/api/document/Upload HTTP/1.1
Host: localhost:10050
Connection: keep-alive
Content-Length: 2
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJDb3JyZWxhdGlvbklkIjoiZDZlNzE0OTUtZTM2MS00YTkxLWExNWUtNTc5ODY5NjhjNDkxIiwiVXNlcklkIjoiMSIsIlVzZXJOYW1lIjoiWjk5OTkiLCJXb3Jrc3BhY2UiOiJRc3lzVFRAU09BVEVNUCIsIk1hbmRhbnRJZCI6IjUwMDEiLCJDb3N0Q2VudGVySWQiOiIxMDAxIiwiTGFuZ3VhZ2VDb2RlIjoiMSIsIkxhbmd1YWdlU3RyaW5nIjoiZGUtREUiLCJTdGF0aW9uSWQiOiI1NTAwMSIsIk5hbWUiOiJJQlMtU0VSVklDRSIsImlzcyI6InNlbGYiLCJhdWQiOiJodHRwOi8vd3d3LmV4YW1wbGUuY29tIiwiZXhwIjoxNDk1Mzc4Nzg4LCJuYmYiOjE0OTUzNzUxODh9.5ZP7YkEJ2GcWX9ce-kLaWJ79P4d2iCgePKLqMaCe-4A
Origin: http://localhost:10050
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Content-Type: multipart/form-data
Accept: application/json, text/plain, */*
Referer: http://localhost:10050/fmea/1064001/content
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
The issue I am facing is that the file is always null in the controller.
Please some one help me in figuring out the issue.
Thanks in advance.
You don't need to use 'multipart/form-data' with FormData
In Angular 2 component:
<input type="file" class="form-control" name="documents" (change)="onFileChange($event)" />
onFileChange(event: any) {
let fi = event.srcElement;
if (fi.files && fi.files[0]) {
let fileToUpload = fi.files[0];
let formData:FormData = new FormData();
formData.append(fileToUpload.name, fileToUpload);
let headers = new Headers();
headers.append('Accept', 'application/json');
// DON'T SET THE Content-Type to multipart/form-data, You'll get the Missing content-type boundary error
let options = new RequestOptions({ headers: headers });
this.http.post(this.baseUrl + "upload/", formData, options)
.subscribe(r => console.log(r));
}
}
On API side
[HttpPost("upload")]
public async Task<IActionResult> Upload()
{
var files = Request.Form.Files;
foreach (var file in files)
{
// to do save
}
return Ok();
}
Update:
After some clarification from the ASP.NET Core Team, it has to do with the compat switch in the Startup class. If you set it like this:
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
then you are fine if you also remove the [FromForm] Attribute from the file parameter.
Old Post:
I ran into a similiar problem with the latest ASP.NET Core WebApi (2.1.2 at the time of this post), which I could only solve by accident after an hour of stackoverflowing, research and lots of trial and error. I was posting the file from an Angular 6 application like this:
const formData: FormData = new FormData();
formData.append('file', file, file.name);
const req = new HttpRequest('POST', 'upload-url', formData, {
reportProgress: true
});
this.http.request(req).subscribe(...) // omitted the rest
The problem was, that the IFormFile file Action method parameter was always null even when putting [FromForm] in front it. The [FromForm] was necessary due to api controller behavior changes in ASP.NET Core 2.1, where [FromBody] becomes the default for api controllers. Strangely, it still didn't work, the value stayed null.
I finally solved it by explicitly stating the name of the form content parameter using the attribute, like this:
public async Task<IActionResult> UploadLogo([FromForm(Name = "file")] IFormFile file)
{
...
}
Now the file upload was bound correctly to the controller parameter. I hope this might help someone in the future as it almost cost me my sanity :D
Another way with dotnet core, you can use IFormFile interface, with setting default headers:
Angular 2
let formData = new FormData();
formData.append("file", yourUploadFile);
this.http.post("your_api_path", formData).subscribe(r => console.log(r));
Dotnet Core
[HttpPost]
[Route("/your_api_path")]
public async Task<IActionResult> Upload(IFormFile file) {
//...next awaiters...
}
If you want send multiple files, you can use ICollection<IFormFile> as Upload params.
For sending multiple properties you can use custom model object and IFormFile will be one of properties.
Hope it help!
Core 3.1
[HttpPost("upload-bulk-excel"), DisableRequestSizeLimit]
public IActionResult Upload()
{
try
{
var file = Request.Form.Files[0];
Angular 8
uploadExcelFile() {
const formData = new FormData();
formData.append('file', this.fileToUpload, this.fileToUpload.name);
this._httpClient.post(`${environment.apiUrl}/upload/upload-bulk-excel`, formData, {reportProgress: true, observe: 'events'})
.subscribe(event => {
if (event.type === HttpEventType.UploadProgress)
this.uploadProgress = Math.round(100 * event.loaded / event.total);
else if (event.type === HttpEventType.Response) {
this.uploadMessage = 'Upload success.';
alert('File uploaded successfully');
}
});
}
So I've searched all over for the answer, can't seem to find one
I'm making a nodejs app, and at one point I want to POST data in the form of a JSON to my server. Here is what I have in my 'users.js' on the server:
/** Updates the array with the new user information **/
exports.addUser = function(req, res) {
var user = req.body;
var userDB = findByEmail(user.email);
if(userDB == -1){
//New user - send back user information
users.push(user);
res.send(JSON.stringify(user));
Whenever I check on Chrome - I see that although I am SENDING the corrent information, the response is just {}
Here's what I have on the client.js
var user = {email:userEmail,username:userUsername,password:userPassword};
request = new XMLHttpRequest();
if (request)
{
request.open("POST","users");
request.onreadystatechange = function()
{
if (request.readyState == 4 &&
request.status == 200){
result = JSON.parse(request.responseText);
if(result == 0)
{
//Result came back as 0 - Old User, wrong password
}
if(result == 1){
//Result came back as 1 - Correct password, old User
}
if(result.email == userEmail){
//Result came back as -1 - New User
}
}
}
request.send(JSON.stringify(user));
}
Now everything here I think is correct - because I see the HTTP request coming out good.
However it appears in the 'payload' part of the request - is this normal?
Here is my requires on server.js -
var express = require('express');
var questions = require('./Quotes');
var scoreboard = require('./scoreboard');
var users = require('./users');
var app = express();//.createServer();
app.configure(function () {
app.use(express.logger('dev'));
app.use(express.bodyParser());//it will parse json request bodies (as well as others), and place the result in req.body:
});
app.post('/users',users.addUser);
Finally here is what the Chrome shows when I test it -
I forgot to add - this is how the request looks on Chrome -
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:70
Content-Type:application/xml
Host:whosaidit.eu01.aws.af.cm
Origin:http://whosaidit.eu01.aws.af.cm
Referer:http://whosaidit.eu01.aws.af.cm/
User-Agent:Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36
Request Payloadview source
{email:oreporan#gmail.com, username:orepor, password:yotamp}
email: "john#gmail.com"
password: "password"
username: "john123"
Please help!
Thanks
There's a couple of things I'm noticing:
Even though you are encoding your client side data to JSON, you're not telling the server that it's JSON that you are sending to it. Before you send your request, set the content-type:
request.setRequestHeader('content-type', 'application/json');
request.send(...);
How is findByEmail() implemented? If it queries a database, the chances are that it's implemented asynchronously, meaning that you cannot expect it to return a useful return code.
This piece of code is confusing:
if (userDB == -1) {
users.push(user); // what is 'users'? what's the use of this code?
res.send(JSON.stringify(user)); // you send back an object, but in your
// client side code you are expecting a
// number: "if (result == 0)"
}
I'm making a HTTP request and listen for "data":
response.on("data", function (data) { ... })
The problem is that the response is chunked so the "data" is just a piece of the body sent back.
How do I get the whole body sent back?
request.on('response', function (response) {
var body = '';
response.on('data', function (chunk) {
body += chunk;
});
response.on('end', function () {
console.log('BODY: ' + body);
});
});
request.end();
Over at https://groups.google.com/forum/?fromgroups=#!topic/nodejs/75gfvfg6xuc, Tane Piper provides a good solution very similar to scriptfromscratch's, but for the case of a JSON response:
request.on('response',function(response){
var data = [];
response.on('data', function(chunk) {
data.push(chunk);
});
response.on('end', function() {
var result = JSON.parse(data.join(''))
return result
});
});`
This addresses the issue that OP brought up in the comments section of scriptfromscratch's answer.
I never worked with the HTTP-Client library, but since it works just like the server API, try something like this:
var data = '';
response.on('data', function(chunk) {
// append chunk to your data
data += chunk;
});
response.on('end', function() {
// work with your data var
});
See node.js docs for reference.
In order to support the full spectrum of possible HTTP applications, Node.js's HTTP API is very low-level. So data is received chunk by chunk not as whole.
There are two approaches you can take to this problem:
1) Collect data across multiple "data" events and append the results
together prior to printing the output. Use the "end" event to determine
when the stream is finished and you can write the output.
var http = require('http') ;
http.get('some/url' , function (resp) {
var respContent = '' ;
resp.on('data' , function (data) {
respContent += data.toString() ;//data is a buffer instance
}) ;
resp.on('end' , function() {
console.log(respContent) ;
}) ;
}).on('error' , console.error) ;
2) Use a third-party package to abstract the difficulties involved in
collecting an entire stream of data. Two different packages provide a
useful API for solving this problem (there are likely more!): bl (Buffer
List) and concat-stream; take your pick!
var http = require('http') ;
var bl = require('bl') ;
http.get('some/url', function (response) {
response.pipe(bl(function (err, data) {
if (err) {
return console.error(err)
}
data = data.toString() ;
console.log(data) ;
}))
}).on('error' , console.error) ;
The reason it's messed up is because you need to call JSON.parse(data.toString()). Data is a buffer so you can't just parse it directly.
If you don't mind using the request library
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Print the google web page.
}
})
If you are dealing with non-ASCII contents(Especially for Chinese/Japanese/Korean characters, no matter what encoding they are), you'd better not treat chunk data passed over response.on('data') event as string directly.
Concatenate them as byte buffers and decode them in response.on('end') only to get the correct result.
// Snippet in TypeScript syntax:
//
// Assuming that the server-side will accept the "test_string" you post, and
// respond a string that concatenates the content of "test_string" for many
// times so that it will triggers multiple times of the on("data") events.
//
const data2Post = '{"test_string": "swamps/沼泽/沼澤/沼地/늪"}';
const postOptions = {
hostname: "localhost",
port: 5000,
path: "/testService",
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data2Post) // Do not use data2Post.length on CJK string, it will return improper value for 'Content-Length'
},
timeout: 5000
};
let body: string = '';
let body_chunks: Array<Buffer> = [];
let body_chunks_bytelength: number = 0; // Used to terminate connection of too large POST response if you need.
let postReq = http.request(postOptions, (res) => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', (chunk: Buffer) => {
body_chunks.push(chunk);
body_chunks_bytelength += chunk.byteLength;
// Debug print. Please note that as the chunk may contain incomplete characters, the decoding may not be correct here. Only used to demonstrating the difference compare to the final result in the res.on("end") event.
console.log("Partial body: " + chunk.toString("utf8"));
// Terminate the connection in case the POST response is too large. (10*1024*1024 = 10MB)
if (body_chunks_bytelength > 10*1024*1024) {
postReq.connection.destroy();
console.error("Too large POST response. Connection terminated.");
}
});
res.on('end', () => {
// Decoding the correctly concatenated response data
let mergedBodyChunkBuffer:Buffer = Buffer.concat(body_chunks);
body = mergedBodyChunkBuffer.toString("utf8");
console.log("Body using chunk: " + body);
console.log(`body_chunks_bytelength=${body_chunks_bytelength}`);
});
});
How about HTTPS chunked response? I've been trying to read a response from an API that response over HTTPS with a header Transfer-Encoding: chunked. Each chunk is a Buffer but when I concat them all together and try converting to string with UTF-8 I get weird characters.