I'm using a simple post request to my backend for a form data and for some reason the body is alwayes empty.
I'm trying to isolate this so i changed the content type to application json and changed the data to json and only this way i can send data.
Client side:
submitForm(event) {
event.preventDefault();
console.log("gggg");
const data = new FormData(event.target);
axios.post("http://localhost:4000/user-form-post",data).then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
Server side:
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
app.use(express.urlencoded());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
app.use(logger('dev'));
app.post('/user-form-post', (req,res) =>{
console.log("dfdf");
console.log(req.body); // alwayes print empty dict {}
res.end();
})
This is not working because it expects jsons(expected behavior):
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
Same behavior with Postman.
You will need to parse your form data from express side. For this you will have to use multer or multiparty. Try something like this. refer the documentation as well
const multiparty = require('multiparty');
app.post('/user-form-post', (req,res) =>{
let form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
Object.keys(fields).forEach(function(name) {
console.log('got field named ' + name);
});
});
})
when it comes to my issue,
i have this front end
const form = new FormData();
form.email = this.email;
form.password = this.password;
console.log("onSubmit -> form", form);
axios.post("http://localhost:3000/register", form )
onSubmit -> form FormData {email: "admin#gmail.com", password: "123"}
but the req.body in backend is empty, and i figured it out that the form in axios.post still need 1 more bracket {} even it's a object. like this
axios.post("http://localhost:3000/register", { form })
After that backend got body like this
req.body = { form: { email: 'admin#gmail.com', password: '123' } }
A problem with request body when you post data is data type .
I have recently a problem with Postman .
You should post data with type x-www-form-urlencoded or raw->JSON to fix the problem.
Goodluck.
You are using:
app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
Please, also use given below line code but first install multer and write the code in top of your application:
var multer = require('multer');
var upload = multer();
app.use(express.json());
Faced the same issue , spent 2 days . Here are the solutions i found :
my request payload had JSON.stringify() , it will make body as {} empty object . when i removed JSON.stringify() and sent request it worked .
Content type should be multipart-form :boundary -----
Now if i externally set it to multipart-form , boundary thing was missing.
for few people it worked when you set content-type as false / undefined , boundary thing got added up,but not for me .
Even though i followed all steps and sending FormData as payload, payload was request payload object in network tab and was not FormData object , my request failed with 500 .
i tried the below code , its react + typescript (make necessary changes to avoid syntax errors)
import QueryString from 'qs';
import { ApiParams } from './xyzfile';
import { ApiHandlerRawType } from './types/xyzfile';
const setDefaultOptions = ({
method = '',
url = '',
params = {},
data = {},
signal = null,
headers = new Headers(),
...options
} = {}) => ({
method,
url,
params,
signal,
headers,
data,
...options
});
const setData = ({ method, data, ...options }: ApiHandlerRawType) => {
const option = options;
if (method !== 'GET' && option.isStreamData) {
option.body = data;
}
return {
method,
...option
};
};
const addRequestHeaders = ({ headers = new Headers(), ...options }) => {
const { existingHeaders }: ApiHandlerRawType = options;
if (existingHeaders) {
Object.entries(existingHeaders).forEach(([key, value]) => {
if (key !== 'Content-Type') headers.set(key, value);
});
}
return {
headers,
...options
};
};
export const ApiHandlerRaw = ({
url,
...originalOptions
}: ApiHandlerRawType): Promise<Response> => {
const options = setData(
addRequestHeaders(setDefaultOptions(originalOptions))
);
return fetch(url || '', options)
.then(response => {
if (!response.ok) throw new Error(response.statusText);
return Promise.resolve(response);
})
.catch(err => Promise.reject(err));
};
export const FileUploadApiHandler = async ({
headers,
...options
}: ApiHandlerRawType): Promise<Response | Blob> => {
const response = await ApiHandlerRaw({
headers,
isStreamData: true,
...options
});
return response;
};
export const fileApiService = ({
url,
method,
qsObject,
headers,
reqObjectAsStreamData
}: ApiParams): Promise<Response> => {
const qs = QueryString.stringify(qsObject, { addQueryPrefix: true });
const urlPath = `${url}${qs}`;
const data = reqObjectAsStreamData;
const existingHeaders = headers;
return FileUploadApiHandler({
url: urlPath,
method,
data,
existingHeaders
}) as Promise<Response>;
};
send the required variables from fileApiService . existingHeaders would be your app headers , eg : token / ids ... etc . data in fileApiService is the body .
I have also faced the same issue in the published code.
But I have fixed this issue by using the below code highlighted in the attached image :-
enter image description here
There is no use of "Content-Type" to fix this issue.
Hope you fix your issue by using the above code snippets.
Related
When I paste the endpoint URL with query directly inside the axios.get(), it responds correctly and I can see the json object returned. (i.e axios.get(http://localhost:3000/api/products/product_search?secretKey=${secret}&id=${blabla})). However, if I call the url with the summonerByNameUrl method, it crashes when I make a request. What is the problem in my code?
Crash report:
...
data: '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>Cannot GET /[object%20Object]</pre>\n' +
'</body>\n' +
'</html>\n'
},
isAxiosError: true,
toJSON: [Function: toJSON]
Code:
config.js
const summonerByNameUrl = (summonerName) => `${URL(hidden)}${summonerName}`;
module.exports = {
summonerByNameUrl
}
summoner.js
const config = require('../config');
const axios = require('axios');
const getSummonerByName = async (summonerName) => {
const res = await axios.get(config.summonerByNameUrl(summonerName));
return res.data;
}
const summonerParser = async (req, res) => {
if(!req.query.secretKey)
return res.status(403).json({error: 'missing secret key.'})
let data = await getSummonerByName(req.query)
return res.status(200).json(data);
}
module.exports = {
getSummonerByName,
summonerParser
}
products.js
var express = require('express');
var axios = require('axios')
var router = express.Router();
const summoner = require('../services/summoner');
router.get('/product_search', summoner.summonerParser)
module.exports = router;
app.js
...
app.use('/api/products', productsRouter);
...
You're calling your function with getSummonerByName(req.query) where it is clear from the lines just before that req.query is an object and not a string. When objects are used in a string-context (like your URL), they become "[object Object]", hence the error.
Taking some guesses here but it seems you want to forward some req.query information to the Axios call as query params. Try this instead...
const PRODUCT_SEARCH_URL = "http://localhost:3000/api/products/product_search"
const getSummonerByName = async ({ secretKey, id }) => {
const { data } = await axios.get(PRODUCT_SEARCH_URL, {
params: { secretKey, id }
})
return data
}
If you've got a helper function that returns the base URL (ie http://localhost:3000/api/products/product_search) then by all means, use that instead of a string literal in the Axios call.
The req.query is a Object, not a string.
You can try map the req.query object to make a string. Something like that:
Object.keys(req.query).map(key => {
return key + '=' + req.query[key]
}).join('&')
This code return a string like that: 'id=1&name=test', so you can pass to the endpoint.
I am building mock restful API to learn better. I am using MongoDB and node.js, and for testing I use postman.
I have a router that sends update request router.patch. In my DB, I have name (string), price (number) and imageProduct (string - I hold the path of the image).
I can update my name and price objects using raw-format on the postman, but I cannot update it with form-data. As I understand, in raw-form, I update the data using the array format. Is there a way to do it in form-data? The purpose of using form-data, I want to upload a new image because I can update the path of productImage, but I cannot upload a new image public folder. How can I handle it?
Example of updating data in raw form
[ {"propName": "name"}, {"value": "test"}]
router.patch
router.patch('/:productId', checkAuth, (req, res, next) => {
const id = req.params.productId;
const updateOps = {};
for (const ops of req.body) {
updateOps[ops.propName] = ops.value;
}
Product.updateMany({_id: id}, {$set: updateOps})
.exec()
.then(result => {
res.status(200).json({
message: 'Product Updated',
request: {
type: 'GET',
url: 'http://localhost:3000/products/' + id
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
err: err
});
});
});
Using for...of is a great idea, but you can't use it like you are to loop through an object's properties. Thankfully, Javascript has a few new functions that turn 'an object's properties' into an iterable.
Using Object.keys:
const input = {
firstName: 'Evert',
}
for (const key of Object.keys(input)) {
console.log(key, input[key]);
}
You can also use Object.entries to key both the keys and values:
const input = {
firstName: 'Evert',
}
for (const [key, value] of Object.entries(input)) {
console.log(key, value);
}
I know this answer might be too late to help you but it might help someone in 2020 and beyond.
First, comment out this block:
//const updateOps = {};
//for (const ops of req.body) {
//updateOps[ops.propName] = ops.value;
//}
and change this line:
Product.updateMany({_id: id}, {$set: updateOps})
to this:
Product.updateMany({_id: id}, {$set: req.body})
Everything else is fine. I was having similar issues, but this link helped me:
[What is the difference between ( for... in ) and ( for... of ) statements in JavaScript?
To handle multi-part form data, the bodyParser.urlencoded() or app.use(bodyParser.json());body parser will not work.
See the suggested modules here for parsing multipart bodies.
You would be required to use multer in that case
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();
// for parsing application/json
app.use(bodyParser.json());
// for parsing application/xwww-
app.use(bodyParser.urlencoded({ extended: true }));
//form-urlencoded
// for parsing multipart/form-data
app.use(upload.array());
app.use(express.static('public'));
I am trying to send a post request from a node + express server to my Foxx service on Arangodb.
On the node side :
var route = arangopi + '/edge/' + col.name ;
var body = {data: data, from: fromId, to: toId} ;
console.log('|| body :', route, body) ;
>> || body : http//XXX/_db/my-DB/my-foxx-service/path/to/visitedBy { data: { isBackup: true, text: '', isHint: true, continuance: 3441.5 }, from: 'Drop/27237133', to: 'Bot/41116378' }
return requestify.post (route, body)
On the Foxx side, I receive the request but the logs tell me it has no body :
router.post('/path/to/:param', function (req, res) {
console.log ('|| body :', req.body)
var data = req.body ;
var result = api.DoSomething (req.stateParams.param, data)
res.send(result)
})
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description')
>> || body : [Object { "binarySlice" : function binarySlice() { [native code] }, "asciiSlice" : function asciiSlice() { [native code] }, "base64Slice" : function base64Slice() { [native code] }, "ucs2Slice" : function ucs2Slice() { [native code] }, "hexSlice" : f...
On the node side I also tried the 'request' module.
return request.post(route, {form:body}, function (error, response, body) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
return response ;
});
And I get the same logs from Foxx.
What do I do wrong ?
Here is a screenshot of my operation on the Foxx interface. Is it normal that I cannot specify a request body for testing ?
I think the reason is because you haven't specified in the end point in Foxx that there is a body expected as part of the .post.
It took me a while to work out a way of defining Foxx MicroServices, and I read through a number of ArangoDB example code before I settled on a pattern.
To help you get started, I've provided how I would quickly mock up the Foxx MicroService code in a way that is extensible, allowing you to separate your Routes from your Models.
Use these as examples to get your example working.
I've made assumptions that there are two document collections, 'Drop' and 'Bot' with an edge collection that joins them called 'VisitedBy'.
All these files are stored on your Foxx MicroService:
main.js
'use strict';
module.context.use('/v1/visitedBy', require('./routes/visitedBy'), 'visitedBy');
routes/visitedBy.js
'use strict';
const request = require('#arangodb/request');
const joi = require('joi');
const createRouter = require('#arangodb/foxx/router');
const VisitedBy = require('../models/visitedBy');
const visitedDataSchema = joi.object().required().description('Data that tracks a visited event');
const router = createRouter();
module.exports = router;
/*********************************************
* saveVisitedBy
* Path Params:
* none
* Query Params:
* none
* Body Params:
* body (required) The data that is used to record when something is visited
*/
router.post('/', function (req, res) {
const visitedData = req.body;
const savedData = VisitedBy.saveVisitedByData(VisitedBy.fromClient(visitedData));
if (savedData) {
res.status(200).send(VisitedBy.forClient(savedData));
} else {
res.status(500).send('Data not saved, internal error');
}
}, 'saveVisitedBy')
.body(visitedDataSchema, 'visited data')
.response(VisitedBy.savedDataSchema, 'The response after the data is saved')
.summary('Save visited data')
.description('Save visited data');
models/visitedBy.js
'use strict';
const _ = require('lodash');
const joi = require('joi');
const db = require('#arangodb').db;
const visitedByEdgeCollection = 'VisitedBy';
/*
Schema for a response after saving visitedBy data
*/
const savedDataScema = {
id: joi.string(),
data: joi.object(),
_from: joi.string(),
_to: joi.string()
};
module.exports = {
savedDataSchema: savedDataScema,
forClient(obj) {
// Implement outgoing transformations here
// Remove keys on the base object that do not need to go through to the client
if (obj) {
obj = _.omit(obj, ['_id', '_rev', '_oldRev', '_key']);
}
return obj;
},
fromClient(obj) {
// Implement incoming transformations here
return obj;
},
saveVisitedByData(visitedData) {
const q = db._createStatement({
"query": `
INSERT {
_from: #from,
_to: #to,
data: #data,
date: DATE_NOW()
} IN ##col
RETURN MERGE ({ id: NEW._id }, NEW)
`
});
q.bind('#col', visitedByEdgeCollection);
q.bind('from', visitedData.from);
q.bind('to', visitedData.to);
q.bind('data', visitedData.data);
const res = q.execute().toArray();
return res[0];
}
};
Your service should look like this in the Swagger interface:
You can learn more about using joi to define data structures here.
It takes a bit getting used to joi, but once you get some good working examples you can define great data definitions for incoming and outgoing data.
I hope this helps, it was difficult for me getting a basic MicroService code model that made it clear how things operated, I'm sure a lot can be done for this example but it should be a good starting spot.
As David Thomas explained in his answer, I needed to specify a body format in my router code (Foxx side).
In short :
const bodySchema = joi.object().required().description('Data Format');
router.post('/path/to/:param', function (req, res) {
var data = req.body ;
var result = api.DoSomething (req.stateParams.param, data)
res.send(result)
})
.body(bodySchema, 'Body data')
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description')
The title should be pretty self explanetory.
For debugging purposes, I would like express to print the response code and body for every request serviced. Printing the response code is easy enough, but printing the response body is trickier, since it seems the response body is not readily available as a property.
The following does NOT work:
var express = require('express');
var app = express();
// define custom logging format
express.logger.format('detailed', function (token, req, res) {
return req.method + ': ' + req.path + ' -> ' + res.statusCode + ': ' + res.body + '\n';
});
// register logging middleware and use custom logging format
app.use(express.logger('detailed'));
// setup routes
app.get(..... omitted ...);
// start server
app.listen(8080);
Of course, I could easily print the responses at the client who emitted the request, but I would prefer doing at the server side too.
PS: If it helps, all my responses are json, but hopefully there is a solution that works with general responses.
Not sure if it's the simplest solution, but you can write a middleware to intercept data written to the response. Make sure you disable app.compress().
function logResponseBody(req, res, next) {
var oldWrite = res.write,
oldEnd = res.end;
var chunks = [];
res.write = function (chunk) {
chunks.push(chunk);
return oldWrite.apply(res, arguments);
};
res.end = function (chunk) {
if (chunk)
chunks.push(chunk);
var body = Buffer.concat(chunks).toString('utf8');
console.log(req.path, body);
oldEnd.apply(res, arguments);
};
next();
}
app.use(logResponseBody);
I ran into an issue using the approach suggested by Laurent. Sometimes chunk is a string, and therefore causes problems in the call to Buffer.concat(). Anyways, I found a slight modification fixed things:
function logResponseBody(req, res, next) {
var oldWrite = res.write,
oldEnd = res.end;
var chunks = [];
res.write = function (chunk) {
chunks.push(new Buffer(chunk));
oldWrite.apply(res, arguments);
};
res.end = function (chunk) {
if (chunk)
chunks.push(new Buffer(chunk));
var body = Buffer.concat(chunks).toString('utf8');
console.log(req.path, body);
oldEnd.apply(res, arguments);
};
next();
}
app.use(logResponseBody);
The above accepted code has issues with ES6.
Use the below code
function logReqRes(req, res, next) {
const oldWrite = res.write;
const oldEnd = res.end;
const chunks = [];
res.write = (...restArgs) => {
chunks.push(Buffer.from(restArgs[0]));
oldWrite.apply(res, restArgs);
};
res.end = (...restArgs) => {
if (restArgs[0]) {
chunks.push(Buffer.from(restArgs[0]));
}
const body = Buffer.concat(chunks).toString('utf8');
console.log({
time: new Date().toUTCString(),
fromIP: req.headers['x-forwarded-for'] ||
req.connection.remoteAddress,
method: req.method,
originalUri: req.originalUrl,
uri: req.url,
requestData: req.body,
responseData: body,
referer: req.headers.referer || '',
ua: req.headers['user-agent']
});
// console.log(body);
oldEnd.apply(res, restArgs);
};
next();
}
module.exports = logReqRes;
You can use express-winston and configure using:
expressWinston.requestWhitelist.push('body');
expressWinston.responseWhitelist.push('body');
Example in coffeescript:
expressWinston.requestWhitelist.push('body')
expressWinston.responseWhitelist.push('body')
app.use(expressWinston.logger({
transports: [
new winston.transports.Console({
json: true,
colorize: true
})
],
meta: true, // optional: control whether you want to log the meta data about the request (default to true)
msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
expressFormat: true, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true
colorStatus: true, // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true
ignoreRoute: function (req, res) { return false; } // optional: allows to skip some log messages based on request and/or response
}));
This solution might not be heavyweight enough for some use cases, but I think it's the simplest. It's also typescript compatible. If you only want logging for JSON responses, all you have to do is substitute the send method with the json method in the code below. Note, I took inspiration from Jonathan Turnock's answer, but made it simpler.
app.use((req, res, next) => {
let send = res.send;
res.send = c => {
console.log(`Code: ${res.statusCode}`);
console.log("Body: ", c);
res.send = send;
return res.send(c);
}
next();
});
I found the simplest solution to this problem was to add a body property to the res object when sending the response, which can later be accessed by the logger. I add this to my own namespace that I maintain on the req and res objects to avoid naming collisions. e.g.
res[MY_NAMESPACE].body = ...
I have a utility method that formats all responses to my standardized API/JSON response, so adding this one liner there exposed the response body when the logging gets triggered by onFinished event of res.
Most of the suggestions seemed a little sledgehammer, Spent some time with this issue tonight and wrote up my findings after digging into a few libs to help make something bespoke.
//app.js
...
app.use(requestLoggerMiddleware({ logger: console.log }));
app.get(["/", "/api/health"], (req, res) => {
res.send({ message: "OK", uptime: process.uptime() });
...
});
// middleware.js
/**
* Interceptor function used to monkey patch the res.send until it is invoked
* at which point it intercepts the invokation, executes is logic such as res.contentBody = content
* then restores the original send function and invokes that to finalize the req/res chain.
*
* #param res Original Response Object
* #param send Original UNMODIFIED res.send function
* #return A patched res.send which takes the send content, binds it to contentBody on
* the res and then calls the original res.send after restoring it
*/
const resDotSendInterceptor = (res, send) => (content) => {
res.contentBody = content;
res.send = send;
res.send(content);
};
/**
* Middleware which takes an initial configuration and returns a middleware which will call the
* given logger with the request and response content.
*
* #param logger Logger function to pass the message to
* #return Middleware to perform the logging
*/
const requestLoggerMiddleware = ({ logger }) => (req, res, next) => {
logger("RECV <<<", req.method, req.url, req.hostname);
res.send = resDotSendInterceptor(res, res.send);
res.on("finish", () => {
logger("SEND >>>", res.contentBody);
});
next();
};
module.exports = { requestLoggerMiddleware };
Full working example and article in the git repo
https://github.com/JonathanTurnock/ReqResLoggingExample
I actually made this nifty little npm to solve this exact problem, hope you like it!
https://www.npmjs.com/package/morgan-body
May be this would help someone who is looking to get the response logged
So, we use the middleware to intercept the request just before being served to the client. Then if we are using res.send method to send the data, override the method in the middleware and make sure to console log the body. If you are planning to use res.send alone then this should work fine, but incase if you use res.end or res.sendFile, then overwrite those methods and log only the required things (obviously logging the entire octet stream of file should never be logged for perfomance purposes.
Here I use pino as the logger. Created it as singleton service.
// LoggingResponseRouter.js
var loggingResponseRouter = require('express').Router();
var loggingService = require('./../service/loggingService');
var appMethodInstance = require('./../constants/appMethod');
var path = require('path');
var fs = require('fs');
var timeZone = require('moment-timezone');
var pino = require('pino')();
loggingResponseRouter.use((req, res, next) => {
// set the fileName it needs to log
appMethodInstance.setFileName(__filename.substring(__filename.lastIndexOf(path.sep) + 1, __filename.length - 3));
//loggingService.debugAndInfolog().info('logging response body', appMethodInstance.getFileName());
let send = res.send;
res.send = function(body){
loggingService.debugAndInfolog().info('Response body before sending: ', body);
send.call(this, body);
}
next();
});
module.exports = loggingResponseRouter;
Main file - Main.js
const corsRouter = require('./app/modules/shared/router/corsRouter');
const logRequestRouter = require('./app/modules/shared/router/loggingRequestRouter');
const loggingResponseRouter = require('./app/modules/shared/router/loggingResponseRouter');
const express = require('express');
var path = require('path');
const app = express();
// define bodyparser middleware
const bodyParser = require('body-parser');
const port = process.env.PORT || 3000;
// Now use the middleware prior to any others
app.use(bodyParser.json());
// use this to read url form encoded values as wwell
app.use(bodyParser.urlencoded({extended:true}));
console.log('before calling cors router in main js');
app.use(corsRouter);
app.use(logRequestRouter);
app.use(loggingResponseRouter);
app.get('/api', (req, res) => {
console.log('inside api call');
res.send('aapi');
});
app.listen(port, () => {
console.log('starting the server');
});
And this is the loggingService - loggingService.js
var pino = require('pino');
var os = require('os');
var appMethodInstance = require('./../constants/appMethod');
var pinoPretty = require('pino-pretty');
var moment = require('moment');
var timeZone = require('moment-timezone');
class Logger{
constructor(){
this.appName = 'Feedback-backend';
this.filenameval = '';
}
getFileName(){
console.log('inside get filename');
console.log(appMethodInstance.getFileName());
if(appMethodInstance.getFileName() === null || appMethodInstance.getFileName() === undefined){
this.filenameval = 'bootstrapping...'
}else {
this.filenameval = appMethodInstance.getFileName();
}
console.log('end');
return this.filenameval;
}
debugAndInfolog(){
return pino({
name: 'feedback-backend',
base: {
pid: process.pid,
fileName: this.getFileName(),
moduleName: 'modulename',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
hostName: os.hostname()
},
level: 'info',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
messageKey: 'logMessage',
prettyPrint: {
messageKey: 'logMessage'
}
});
}
errorAndFatalLog(){
return pino({
name: 'feedback-backend',
base: {
pid: process.pid,
fileName: this.getFileName(),
moduleName: 'modulename',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
hostName: os.hostname()
},
level: 'error',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
prettyPrint: {
messageKey: 'FeedbackApp'
}
});
}
}
module.exports = new Logger();
Typescript solution based on Laurent's answer:
import { NextFunction, Request, Response } from 'express-serve-static-core';
//...
app.use(logResponseBody);
function logResponseBody(req: Request, res: Response, next: NextFunction | undefined) {
const [oldWrite, oldEnd] = [res.write, res.end];
const chunks: Buffer[] = [];
(res.write as unknown) = function(chunk) {
chunks.push(Buffer.from(chunk));
(oldWrite as Function).apply(res, arguments);
};
res.end = function(chunk) {
if (chunk) {
chunks.push(Buffer.from(chunk));
}
const body = Buffer.concat(chunks).toString('utf8');
console.log(new Date(), ` ↪ [${res.statusCode}]: ${body}`);
(oldEnd as Function).apply(res, arguments);
};
if (next) {
next();
}
}
I have similar need to this question.
Based on accepted answer, I modify it with proxy and trace response body only when it's json.
const traceMiddleware = (req, res, next) => {
const buffers = []
const proxyHandler = {
apply(target, thisArg, argumentsList) {
const contentType = res.getHeader('content-type')
if (
typeof contentType === 'string' && contentType.includes('json') && argumentsList[0]
) {
buffers.push(argumentsList[0])
}
return target.call(thisArg, ...argumentsList)
}
}
res.write = new Proxy(res.write, proxyHandler)
res.end = new Proxy(res.end, proxyHandler)
res.on('finish', () => {
// tracing logic inside
trace(req, res, Buffer.concat(buffers).toString('utf8'))
})
next()
}
I know how to get the params for queries like this:
app.get('/sample/:id', routes.sample);
In this case, I can use req.params.id to get the parameter (e.g. 2 in /sample/2).
However, for url like /sample/2?color=red, how can I access the variable color?
I tried req.params.color but it didn't work.
So, after checking out the express reference, I found that req.query.color would return me the value I'm looking for.
req.params refers to items with a ':' in the URL and req.query refers to items associated with the '?'
Example:
GET /something?color1=red&color2=blue
Then in express, the handler:
app.get('/something', (req, res) => {
req.query.color1 === 'red' // true
req.query.color2 === 'blue' // true
})
Use req.query, for getting he value in query string parameter in the route.
Refer req.query.
Say if in a route, http://localhost:3000/?name=satyam you want to get value for name parameter, then your 'Get' route handler will go like this :-
app.get('/', function(req, res){
console.log(req.query.name);
res.send('Response send to client::'+req.query.name);
});
Query string and parameters are different.
You need to use both in single routing url
Please check below example may be useful for you.
app.get('/sample/:id', function(req, res) {
var id = req.params.id; //or use req.param('id')
................
});
Get the link to pass your second segment is your id example: http://localhost:port/sample/123
If you facing problem please use Passing variables as query string using '?' operator
app.get('/sample', function(req, res) {
var id = req.query.id;
................
});
Get link your like this example: http://localhost:port/sample?id=123
Both in a single example
app.get('/sample/:id', function(req, res) {
var id = req.params.id; //or use req.param('id')
var id2 = req.query.id;
................
});
Get link example: http://localhost:port/sample/123?id=123
Update: req.param() is now deprecated, so going forward do not use this answer.
Your answer is the preferred way to do it, however I thought I'd point out that you can also access url, post, and route parameters all with req.param(parameterName, defaultValue).
In your case:
var color = req.param('color');
From the express guide:
lookup is performed in the following order:
req.params
req.body
req.query
Note the guide does state the following:
Direct access to req.body, req.params, and req.query should be
favoured for clarity - unless you truly accept input from each object.
However in practice I've actually found req.param() to be clear enough and makes certain types of refactoring easier.
#Zugwait's answer is correct. req.param() is deprecated. You should use req.params, req.query or req.body.
But just to make it clearer:
req.params will be populated with only the route values. That is, if you have a route like /users/:id, you can access the id either in req.params.id or req.params['id'].
req.query and req.body will be populated with all params, regardless of whether or not they are in the route. Of course, parameters in the query string will be available in req.query and parameters in a post body will be available in req.body.
So, answering your questions, as color is not in the route, you should be able to get it using req.query.color or req.query['color'].
The express manual says that you should use req.query to access the QueryString.
// Requesting /display/post?size=small
app.get('/display/post', function(req, res, next) {
var isSmall = req.query.size === 'small'; // > true
// ...
});
const express = require('express')
const bodyParser = require('body-parser')
const { usersNdJobs, userByJob, addUser , addUserToCompany } = require ('./db/db.js')
const app = express()
app.set('view engine', 'pug')
app.use(express.static('public'))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.get('/', (req, res) => {
usersNdJobs()
.then((users) => {
res.render('users', { users })
})
.catch(console.error)
})
app.get('/api/company/users', (req, res) => {
const companyname = req.query.companyName
console.log(companyname)
userByJob(companyname)
.then((users) => {
res.render('job', { users })
}).catch(console.error)
})
app.post('/api/users/add', (req, res) => {
const userName = req.body.userName
const jobName = req.body.jobName
console.log("user name = "+userName+", job name : "+jobName)
addUser(userName, jobName)
.then((result) => {
res.status(200).json(result)
})
.catch((error) => {
res.status(404).json({ 'message': error.toString() })
})
})
app.post('/users/add', (request, response) => {
const { userName, job } = request.body
addTeam(userName, job)
.then((user) => {
response.status(200).json({
"userName": user.name,
"city": user.job
})
.catch((err) => {
request.status(400).json({"message": err})
})
})
app.post('/api/user/company/add', (req, res) => {
const userName = req.body.userName
const companyName = req.body.companyName
console.log(userName, companyName)
addUserToCompany(userName, companyName)
.then((result) => {
res.json(result)
})
.catch(console.error)
})
app.get('/api/company/user', (req, res) => {
const companyname = req.query.companyName
console.log(companyname)
userByJob(companyname)
.then((users) => {
res.render('jobs', { users })
})
})
app.listen(3000, () =>
console.log('Example app listening on port 3000!')
)
you can simply use req.query for get query parameter:
app.get('/', (req, res) => {
let color1 = req.query.color1
let color2 = req.query.color2
})
The url module provides utilities for URL resolution and parsing. URL parse without using Express:
const url = require('url');
const queryString = require('querystring');
let rawUrl = 'https://stackoverflow.com/?page=2&size=3';
let parsedUrl = url.parse(rawUrl);
let parse = queryString.parse(parsedUrl.query);
// parse = { page: '2', size: '3' }
Another way:
const url = require('url');
app.get('/', (req, res) => {
const queryObject = url.parse(req.url,true).query;
});
url.parse(req.url,true).query returns { color1: 'red', color2: 'green' }.
url.parse(req.url,true).host returns 'localhost:8080'.
url.parse(req.url,true).search returns '?color1=red&color2=green'.
Just use the app.get:
app.get('/some/page/here', (req, res) => {
console.log(req.query.color) // Your color value will be displayed
})
You can see it on expressjs.com documentation api:
http://expressjs.com/en/api.html
A nice technique i've started using with some of my apps on express is to create an object which merges the query, params, and body fields of express's request object.
//./express-data.js
const _ = require("lodash");
class ExpressData {
/*
* #param {Object} req - express request object
*/
constructor (req) {
//Merge all data passed by the client in the request
this.props = _.merge(req.body, req.params, req.query);
}
}
module.exports = ExpressData;
Then in your controller body, or anywhere else in scope of the express request chain, you can use something like below:
//./some-controller.js
const ExpressData = require("./express-data.js");
const router = require("express").Router();
router.get("/:some_id", (req, res) => {
let props = new ExpressData(req).props;
//Given the request "/592363122?foo=bar&hello=world"
//the below would log out
// {
// some_id: 592363122,
// foo: 'bar',
// hello: 'world'
// }
console.log(props);
return res.json(props);
});
This makes it nice and handy to just "delve" into all of the "custom data" a user may have sent up with their request.
Note
Why the 'props' field? Because that was a cut-down snippet, I use this technique in a number of my APIs, I also store authentication / authorisation data onto this object, example below.
/*
* #param {Object} req - Request response object
*/
class ExpressData {
/*
* #param {Object} req - express request object
*/
constructor (req) {
//Merge all data passed by the client in the request
this.props = _.merge(req.body, req.params, req.query);
//Store reference to the user
this.user = req.user || null;
//API connected devices (Mobile app..) will send x-client header with requests, web context is implied.
//This is used to determine how the user is connecting to the API
this.client = (req.headers) ? (req.headers["x-client"] || (req.client || "web")) : "web";
}
}