How to detect if request body was sent or not? - javascript

I'm working with express + json body parser. For validation purposes I need to distinguish between
empty object request body was sent by the user ({})
no request body was sent by the user
as I'm trying to provide reasonable error messages, and in one case I need to instruct the user to add missing fields, while in the other case they might not even be aware that the request body should be sent, and the priority is warning them about this.
This to me seems impossible currently, as the json body parser sets request.body to {} in case no request body was provided, reference:
https://github.com/expressjs/body-parser/blob/master/lib/types/json.js#L72
My question is, while using the json body parser, is it possible to detect if the request body was actually sent by the client or not? So how would I implement a function like this:
import { Request } from 'express'
function didTheUserSendRequestBody(request: Request): boolean {
// What do?
}
Which returns true when my endpoint is hit like this:
curl -X POST http://localhost:5000/foo
-H "Content-Type: application/json"
-d '{}'
And false for this:
curl -X POST http://localhost:5000/foo

Body-parser json has option verify . It can check raw data at here.
app.use(express.json({ verify: (req, res, buf, encoding) => {
// check buff is empty ? and attach result to req ?
} }))
with verify is function with 4 argument : "where buf is a Buffer of the raw request body"
https://github.com/expressjs/body-parser#verify

maybe you can just check the submitted content type, if the user doesn't attach it then the default value will be "undefined".
function didTheUserSendRequestBody(request: Request): boolean {
if (!request.get('Content-Type')) {
// return any message
}
}

The easiest way I'm aware of is by checking if req.body is truthy
function didTheUserSendRequestBody(request: Request): boolean {
if(req.body){
// Do stuff
}
}
According to https://developer.mozilla.org/en-US/docs/Glossary/Truthy MDN, an empty object '{}' is truthy. If there is no body sent, it should be undefined when you try to access req.body, which is falsy.
See https://expressjs.com/en/api.html#req for more details

Related

In my JavaScript server, why does my posted data have braces and quotes around it?

I have a simple JavaScript server to process a POST command. But my post data shows up surrounded with braces and quotes. I'm using curl to send this:
curl --data {title:'Odessey',author:'Homer'} localhost:4444/test/add
But my server gets the posted data like this:
{ "{title:'Odessey',author:'Homer'}": '' }
When I send it back to the client, it shows up with spaces removed and quotes converted:
{"{title:'Odessey',author:'Homer'}":""}
Curiously, if I put quotes around the string in curl, I get exactly the same thing:
curl --data "{title:'Odessey',author:'Homer'}" localhost:4444/test/add
I'm running this on Windows 10. The problem doesn't seem to be with curl, because I wrote another (clumsier) server without using express, and this problem didn't show up. Also, I've tried curl's other options for sending data, like --data.ascii and --data.raw, but none of them help. So the problem must be with the express server.
Also, I log the req's Content-Type before processing the POST DATA. By default, it prints out application/x-www-form-urlencoded. At the suggestion of other users, I changed the Content-Type header in the curl statement by saying curl -H Content-Type:application/json, but that didn't help. The log statement confirms that it received this header, but then it fails to read the data at all, logging req.body: {}.
Here's my server code:
const express = require('express');
const app = express();
// taking this block out doesn't help. Without it, I don't get any data.
app.use(
express.urlencoded({ extended: true })
);
// Changing this to express.text() doesn't help. Nor does removing it.
app.use(express.json());
app.post('/test/add', (req, res) => {
let hdr = "Content-Type";
console.log(hdr, req.header(hdr));
console.log("req.body:", req.body);
res.send(req.body);
})
var server = app.listen(4444, function() {
var urlStr = "http://" + host + ':' + port;
console.log ("Example app listeneing at", urlStr);
})
Note: I have updated this slightly since I first posted it.
You should be able to do this by adding the following header: -H 'Content-Type: application/json'. It is receiving it as a GET parameter (something like localhost:4444/test/add?{title:'Odessey',author:'Homer'}).
Mario Martín Moreno had a very useful suggestion to add a -H option to my curl command. This was correct, but the form wasn't quite right. #dandavis was correct that only double quotes were valid for the keys. And #tevemader was right that I was using a header file incorrectly. But the crucial detail was that I needed to escape my quote characters with a backslash. So the full curl line looked like this:
curl --data {\"title\":\"Odessey\",\"author\":\"Homer\"} -H Content-Type:application/json localhost:4444/test/add
Caveats: Single quotes don't work even on just the values, and even with the backslash to escape them. And I still don't know why I needed to do this here, but not with my earlier server that didn't use express. But at least I know how to use curl to send my data. Thank you to everyone who responded with helpful suggestions.

Javascript object coming through empty on server side

I have a client-side script running to send the string "Y" to the server. I set up a console.log on the client-side (which you can see below) and another on the server-side. The one on the client-side works, but the one logs an "empty" object.. it just shows "{}".
How do I get my data to stay in the object?
const status = "Y";
const options = {
method: 'POST',
headers: {
'Content-type': 'application/json'
},
body: status
fetch('/events/<%- event.id %>/prompt', options)
console.log(options.body)
Here's my route for context:
router.route('events/:id/prompt')
.get(catchAsync(events.showPrompt))
.post(catchAsync(events.checkIn))
And my controller:
module.exports.checkIn = async(req, res) => {
console.log(req.body);
}
How do I get the object to come through to the server?
For sending "y" as the content and receiving that in Express, you need two things:
You need to make sure the content-type is set to text/plain on the request.
You need the appropriate middleware that will read that text/plain body.
app.use(express.text())
Then, you will find the body in req.body within any express request handler registered after the above middleware.
You could pick different content-types also such as application/json, the corresponding middleware for that content-type app.use(express.json())` and then format the body data in that format.
It's important to realize that Express does not by itself read the body of an incoming request. It reads the headers, but not the body by default. If you want the body to be read, then you need middleware that is looking for whatever content-type the incoming request has, reads the body, parses it from whatever it's format is and puts the resulting parsed data into req.body. Express comes with a number of built-in middleware for popular content-types.
Status is a string. However body have to take a object with key-value pair. If send like with like below, then you get object which contains status on the backend side.
body: {status: status}
Problem from :
Client : you choose Content-type': 'application/json' , so your body must be json format , something like body : { status } . Make sure you sent exact object with browser debug , because some call api package can change value of request.
Server : Some nodejs framework need parse the value is sent from client before read it (Exp : app.use(express.json()) with Express)

GET request working through Postman but the browser tells me GET request cannot have body

I'm simply trying to send some urlencoded parameters via a GET request using fetch. I'm just trying to print the parameters using Express at the moment, like so:
app.get('/api', function (req, res) {
console.log(req.body);
res.sendStatus(200);
return;
});
This works just fine in Postman using a GET request and x-www-form-urlencoded key-value pairs. The webserver will print all the key-value pairs just fine.
But when I try and use fetch to do the exact same thing I get nothing but problems. I've tried two different methods:
fetch(`http://localhost:3000/api?user=test&password=123`, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
The request does go through using this method, but the webserver only prints {} - an empty object.
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("user", "test");
urlencoded.append("password", "123");
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: urlencoded,
};
fetch("localhost:3000/api", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
The request does not go through using this method, and the browser gives me the error TypeError: Window.fetch: HEAD or GET Request cannot have a body.
This code was generated using the request that works in Postman using the generate code snippets option.
What am I doing wrong?
The parameters in this URL:
http://localhost:3000/api?user=test&password=123
are in the query string, not in the body and thus the content-type does not apply to them - they are properly encoded to be in a URL. In Express, you would access these with req.query. You should see a value for req.query.user and req.query.password in your Exprss request handler.
Note, it is not recommended that you send user credentials in a URL like this because URLs are often present in log files at your ISP, at the recipient server, in proxies, in your browser history, etc... User credentials like this should be sent in POST request over https where the credentials would go encoded in the body (where it won't be logged or saved by intermediaries).
The fetch error is accurate. GET requests do not have a body sent with them. That would be for POST or PUT requests. A GET request is a "get" request for a resource that you specify only with a URL.
You're confusing request body with a query string.
Your second request (you don't need a Content-Type for it)
fetch("http://localhost:3000/api?user=test&password=123");
would be handled by the following Express function:
app.get('/api', function (req, res) {
console.log(req.query); // Note that query, not body is used.
res.sendStatus(200);
return;
});
You can access fields from the query object as req.query.user && req.query.password.
As for having a request body in a GET request: while RFC doesn't explicitly fordbid it, it requires server to not change response based on the contents of the body, i.e. the body in GET has no meaning in the standard, so JS HTTP APIs (both fetch & XmlHttpRequest) deny it.
firstly if you are trying to get some data from your API or others API you should do GET request in order to get your desired data from server for example, if you want to get a specific things like a user or something else you can pass your data in GET request URL using query string or route params.
secondly, if you want to authenticate and send your credentials to the server its not recommended to use GET request as i said earlier GET request simply is for fetching some data from server, so if you want to send your credential or anything else you are better off using POST request to send data to the server and you can't do POST request in the browser, so you have to use something like postman or insomnia in order to send your POST request to the server. i hope it could help you to solve your issue.

Best way to replace left and right parenths in a Get request

For my GET request, my server is rejecting it with status: 403, "HTTP/1.1 403 Forbidden"
My GET request object as is follows :
"{"method":"GET","url":"api/myapi/GETstuff","params":{"from":"2017-06-02","to":"2017-06-02","myData":"DATA (AND SOME/MORE DATA)"}}"
The javascript code is:
function getMyData(params){
var url = 'myapi/getStuff';
var req = {
method: 'GET',
url: url,
params: params
};
$http(req) // details omitted for brevity
.success()
.error();
}
I believe the problem is special characters send to the IIS server (such as parenths, and / char), which I need to encode the params array before the GET request is sent via the Angular $http() service.
In order to encode the left/right parenths, and the / char, I'm trying:
request.params.myData = request.params.myData.replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\//g, '%2F')
However, my server is still rejecting the request.
And I doing something wrong?
You're getting a Forbidden error. Sure you don't need to authenticate or pass a token?
Before getting too manual, I'd suggest trying JSON.stringify(obj) and as a second pass, encodeURIComponent(obj)
If you're posting a valid JSON object, you might want to consider POST instead of GET, if you have backend access.

"Request module" causing issues while doing the get request, on the middleware that uses "connect" in node

I am using request module at the client side to perform a REST get request where middleware is connect which then routes the request to my node server that serves it. The issue is that i tried to use the option json:true while making a request using the request module, So that i do not need to parse and validate the response body i receive. But unfortunately it doesn't reach the server as it fails in the middleware(connect) itself saying "Invalid JSON", since it seems to validate for JSON (when there is no request body) due to the content-type set by the request module.
Here is a request that i make using request module.
request(
{
uri: myurl,
json: true, //issue area
headers: {
//some headers. but no content-type sepcified
}
}
, function (error, response, body) {
console.log(body);
//Here body comes as object if json:true (not for get as it fails in validation at connect middleware itself), else i need to perform JSON.parse(body).
});
Here is definition for json property in the settings of request module (from the documentation).
json - sets body but to JSON representation of value and adds Content-type: application/json header. Additionally, parses the response body as json.
But obviously it is a GET request and there won't be any content-type that i would set (But with json:true option request module seems to be setting it internally).
I could trace this down through connect's json.js snippet below
return function json(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
// check Content-Type
//This guy fails because content-type is set as application/json by request module internally
if ('application/json' != utils.mime(req)) return next();
// flag as parsed
req._body = true;
// parse
limit(req, res, function(err){
if (err) return next(err);
var buf = '';
req.setEncoding('utf8');
req.on('data', function(chunk){ buf += chunk });
req.on('end', function(){
//Here the problem area obviously buf[0] is undefined
if (strict && '{' != buf[0] && '[' != buf[0]) return next(utils.error(400, 'invalid json'));
try {
......
Clearly this is not an issue with connect, but it is probably an incomplete functionality provided with json:true property. I know that i can just set json:false and parse the response (JSON) to javascript object using JSON.parse() but i get this flexibility for other request types (when setting json:true) that i do not need to validate or parse the JSON to object manually instead i get it as object from request module's complete callback.
I would like to know if there is any other option where i can get the response body as object without these issues caused by failure in connect, or any other information on this feature that justifies this behavior with json:true (I couldn't find any), or any other solution that anyone has used, or any satisfactory explanation on this also is appreciated!! Thanks.
Adding an answer if somebody else go through the same issue.
Looking at the request module source, it seems to be a bug which has already been fixed in the latest version of request. So if you are using an older version (mine was 2.0.5) consider upgrading it a newer one.
Older one had the following code, so no matter the json is true and no body has been set explicitly it still used to set the content-type as header.
if (options.json) {
options.headers['content-type'] = 'application/json' //<-- this is being set always
if (typeof options.json === 'boolean') {
if (typeof options.body === 'object') options.body = JSON.stringify(options.body)
} else {
options.body = JSON.stringify(options.json)
}
......
With the latest version this changes:
if (options.json) {
self.json(options.json)
//...More code
//and in json function
this._json = true
if (typeof val === 'boolean') {
if (typeof this.body === 'object') {
this.body = safeStringify(this.body)
self.setHeader('content-type', 'application/json') //<-- sets it only if there is a body
}
} else {
this.body = safeStringify(val)
self.setHeader('content-type', 'application/json')
}

Categories

Resources