Using node-validator to validate multiple properties at once - node.js

I'm building a Node.js proxy with the intent of handling a single POST request and redirecting the payload to two separate endpoints.
Let's say my JSON payload is:
{
"owner":"0ce856fa-f17f-11e2-9062-9b7910849bf4",
"comment":"My super cool comment!",
"photo":"0928536a-53c4-11e3-ba86-4b026f27c637"
}
I need to validate this payload on the proxy endpoint before I send it off; each of these three properties must exist, and both owner and photo must match the regex below. If they don't pass validation, I need to handle the error(s) and return a message back to the user with an appropriate error code.
I've set up a basic Node.js instance with Express and Validator like so in order to accomplish this:
var url = require('url');
var request = require('request');
var express = require('express');
var check = require('validator').check,
sanitize = require('validator').sanitize;
var app = express();
app.use(express.json());
app.use(express.urlencoded());
app.all('*', function(req, res){
if (req.method == "POST")
{
try {
check(req.body.owner, {
is: "<owner> property of type [uuid] is required"
}).is(/\w{8}(?:-\w{4}){3}-\w{12}?/);
} catch (e) {
console.log(e);
res.json({"result":"failed","message":"Your payload didn't pass validation"});
}
}
});
app.listen(9000, function() {
console.log("Server initialized on port 9000");
});
The problem: this is all fine and dandy and works great for a single validation (in this case owner), but e on catch doesn't contain any details about the property that failed validation -- if I set up multiple checks, I'd have no idea which one failed or why.
How can I set up a series of checks and retrieve the custom message I've configured? It talks about using req.onValidationError in the Validator readme, but that looks to be front-end validation, I'm not clear how (if possible) to integrate that up with the server-side code.

try express-validator which provides errors handling like:
var errors = req.validationErrors();

Update, using express-validator:
Per #shawnzhu's suggestion, I implemented express-validator instead; it took a bit of tweaking to get it working with express+connect 3.0, but given it's handling of node-validator errors, it looks like the best way to go (validating headers notwithstanding).
var express = require('express'),
expressValidator = require('express-validator');
var app = express();
app.use(express.json());
app.use(express.urlencoded());
app.use(expressValidator());
req.checkBody("owner", "<owner> property of type [uuid] is required; " + req.body.owner + " is invalid.").is(uuidRegex);
req.checkBody("photo", "<photo> property of type [uuid] is required; " + req.body.owner + " is invalid.").is(uuidRegex);
req.checkBody("comment", "<comment> property can't be empty").notNull().notEmpty();
req.sanitize("comment").trim();
var errors = req.validationErrors();
if (errors)
{
res.json({"result":"failed","errors":errors});
return;
}
To get it working just with node-validator:
It was the inline message validation that was causing problems:
try {
check(req.body.owner, "<owner> property of type [uuid] is required").is(/\w{8}(?:-\w{4}){3}-\w{12}?/);
check(req.body.photo, "<photo> property of type [uuid] is required").is(/\w{8}(?:-\w{4}){3}-\w{12}?/);
check(req.body.comment, "<comment> property can't be empty").notNull().notEmpty();
} catch (e) {
res.json({"result":"failed","message":e.message});
}
This does the job, and validates each property based on the criteria.

Related

JSONSerialization makes incorrect JSON object. Swift

I'm creating an iOS app that connects to a NodeJS server. The get method works fine but the POST method has problems.
var urlRequest = URLRequest(url: "http://localhost:3000/register")
urlRequest.httpMethod = "POST"
let info: [String:Any] = ["username": "username", "password":"username", "email":"username#username.com"]
do {
let jsonInfo = try
JSONSerialization.data(withJSONObject: info, options[])
urlRequest.httpBody = jsonInfo
} catch {
print("ERROR")
return
}
The request gets sent but something goes wrong with JSONSerialization because this is the JSON data that the server gets:
{'{"email":"username#username.com","username":"username","password":"username"}': '' }
This is what I'm going for:
{"email":"username#username.com","username":"username","password":"username"}
This is part of the server code:
const express = require('express');
const bodyParser = require('body-parser');
var app = express();
var allowMethods = function(req, res, next) {
res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE');
next();
}
http.createServer(app).listen(3001);
console.log("Server started");
app.use(bodyParser.urlencoded({extended: true}));
app.use(allowMethods);
const _ = require('lodash');
let b = _.pick(req.body, ['username', 'password', 'email']);
Any ideas on what I'm doing wrong? I'd like to avoid using alamofire if possible. I've tried changing the format of the dictionary but always turns out as:
{ 'the whole dictionary is the key': ''}
I've also tried using pretty print and this was the result:
{ '{\n "email" : "username#username.com",\n "username" : "username",\n "password" : "username"\n}': '' }
Any help is appreciated. Thank you!
EDIT:
I tried Mike Taverne's suggestions.
I changed the server code to use this instead:
app.use(bodyParser.json());
But I receive an empty body from the simulator.
I also added these to the swift code:
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")
But the server also receives an empty body and by empty body I mean the data I'm trying to send is received as empty by the server. When I check the httpBody the data is there but for some reason the server doesn't receive it.
I believe your Swift code is fine. When I did this in a playground:
print(String(data: urlRequest.httpBody!, encoding: String.Encoding.utf8)!)
It printed:
{"username":"username","password":"username","email":"username#username.com"}
I'm not an expert on body-parser, but I think that instead of this:
bodyParser.urlencoded({extended: true})
You should be using this:
bodyParser.json([options]) //not sure which options exactly you need
You may need to set Content-Type: application/json header as well. Refer to the body-parser documentation for more info.
I solved it by changing the data itself. Instead of forcing it to pass JSON I passed it as a string encoded using UTF8
let dataString = "username=username&password=username&email=username#username.com"
urlRequest.httpBody = dataString.data(using: .utf8)
I got the answer by using the method in this post:
HTTP Request in Swift with POST method

Complex NodeJS / Express REGEX routing

I'm trying to create a NodeJS Express API (route) which has the following characteristics:
It has a base path, in my case it is /web/views. This part is a static value and doesn't change for as long as the server is up.
I can do this as follows:
const BASE = '/web/views'; // defined externally/elsewhere
app.get(BASE, function handleRequest(req, res) {
// handle API request...
}
Next, I expect to be provided with a resource. Given the name of this resource, I locate a file and send it to the client.
I can do this as follows:
app.get(BASE + '/:resource', function handleRequest(req, res) {
var resource = req.params.resource;
// handle API request...
}
So on the client, I invoke it this way:
GET /web/views/header
All of this works so far... but my problem is that my 'resource' can actually be a path in itself, such as:
GET /web/views/menu/dashboard
or a longer path, such as:
GET /web/views/some/long/path/to/my/xyz
I was using the following REGEX mapping:
const DEFAULT_REGEX = '/(\*/)?:resource';
or more precisely:
app.get(BASE + DEFAULT_REGEX, function handleRequest(req, res) {
var resource = req.params.resource;
// handle API request...
}
This works with an arbitrary length path between my BASE value and the :resource identifier, but the problem is that my resource variable only has
the xyz portion of the path and not the full path (ie: /some/long/path/to/my/xyz).
I could simply cheat and strip the leading BASE from the req.url, but I though there would be a REGEX rule for it.
If anyone knows how to do such advanced REGEX routing, I'd appreciate it.
Thanks!
Sure, so I think the easiest way is to simply not worry about using Regex, but instead just use a wildcard. You lose the cool params name, but otherwise it works as you're looking for. For example:
const express = require('express');
const app = express();
const port = 3000;
const BASE = '/web/views'
app.get(`${BASE}/*`, (req, res) => {
res.send(req.url);
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
If you hit http://localhost:3000/web/views/path/to/my/resource, in my example the response content will be /web/views/path/to/my/resource, so from there it's some simple string manipulation to pull the bit you want:
let resource = req.url.split('/web/views')[1];
// resource will equal /path/to/my/resource if the above URL is used
Of course you could get fancier with your string parsing to check for errors and such, but you get the idea.
You could even setup a middleware to get that resource piece for other handlers to work from:
app.use(`${BASE}/*`, (req, res, next) => {
const resource = req.url.split(BASE)[1];
req.resource = resource;
next();
});
Then all subsequent routes will have access to req.resource.

Is there a way to track an instance in Node without passing it around everywhere?

I have a singleton logger file. When a request comes into Express, I use middleware to set the request ID.
// Relevant parts of server.js
import express from 'express';
import requestIdMiddleware from './request-id-middleware';
const app = express();
app.use(requestIdMiddleware);
--
// Relevant parts of request-id-middleware.js
const uuid = require('uuid/v4');
const { setRequestId } = require('./logger');
module.exports = function(req, res, next) {
const id = uuid();
req.id = id;
// This sets a static variable on the plain logger object
setRequestId(id);
next();
};
--
// Relevant parts of logger.js
module.exports = {
request_id: null,
setRequestId: id => {
this.request_id = id;
},
log: message => {
// sends a log out using this.request_id, which is shared by all users of the server
}
}
Using the plain object now means everyone is sharing the same value. So despite each request calling setRequestId, it means if the first request takes longer than the second, it may use the second request's ID when referencing logger's value.
It seems I would need to make the logger a class instead of a plain object, instantiate it in my request middleware, and then pass the instance through to ensure unique request IDs across multiple calls from same request. Although unlikely, hoping to find a way around needing to pass a variable down into anything I want to log from.

node express body-parser for application/logplex-1

I am using node express to process POST requests of heroku logging data with body data that is in the application/logplex-1 format (apparently syslog formatted).
In particular, I am using the body-parser module as middleware to parse the POST body.
It works OK to specify app.use(bodyParser.text({ type: 'application/logplex-1' })) to force body-parser to parse the body as text, but the text is just a big block of space-separated information without much structure other than that. Therefore I need to parse the body data further to find and extract what I want.
This is OK, but I'm wondering if there is, perhaps, a better way of parsing the logplex-1 body more directly into something more structured and easier to work with, like JSON. I'm not familiar with logplex-1 or the syslog format, and whether it does indeed have anything more useful structure/metadata in it than is apparent from the text block I'm currently getting.
Any ideas?
I have no experience with logplex or Heroku, but this seems to be working:
var syslogParser = require('glossy').Parse;
var express = require('express');
var app = express();
var server = app.listen(3012);
// Express allows arrays-of-middleware to act as a "single" middleware.
var logplexMiddleware = [
// First, read the message body into `req.body`, making sure it only
// accepts logplex "documents".
require('body-parser').text({ type: 'application/logplex-1' }),
// Next, split `req.body` into separate lines and parse each one using
// the `glossy` syslog parser.
function(req, res, next) {
req.body = (req.body || '').split(/\r*\n/).filter(function(line) {
// Make sure we only parse lines that aren't empty.
return line.length !== 0;
}).map(function(line) {
// glossy doesn't like octet counts to be prepended to the log lines,
// so remove those.
return syslogParser.parse(line.replace(/^\d+\s+/, ''));
});
next();
}
];
// Example endpoint:
app.post('/', logplexMiddleware, function(req, res) {
console.log(req.body);
return res.sendStatus(200);
});
It uses glossy to parse the syslog messages into Javascript objects.
If the amount of data being posted is considerable (>hundreds of K's), it might be better to implement a streaming solution as the code above will first read the entire message body into memory.

Unit testing express routers

I know this has been discussed a couple of times. Anyway, I feel like all the provided solutions don't (perfectly) fit to my requirement(s). I have the following code:
router.js:
------------------
var Router = function(app, resourceName, controller) {
//Create
app.post('/api/' + resourceName, function(req, res) {
console.log('Incoming request: ' + resourceName + ' (POST)');
controller.create(req, res);
});
};
module.exports = Router;
As you can see this is a very "generic" router. It can be instantiated for example in the server like this:
var app = express();
var userController = ...
var userRouter = new Router(app, 'Users', userController);
So I don't have to write a file per resource but I just have one generic router.
I would like to test my generic router but I see some problems:
How to "inject" the app? I could create an instance of Express (var app = express();) but I think a mock would be better (as this is a unit test, not an integration test!). What's the best way to get an appropriate mock?
What exactly should I test? As far as I see my router itself (without integration) isn't doing anything else but console output (not worth to test) and a call of a function (controller.create(req, res);). How should I test if this function is called? Or is there anything else to test?
You should probably make a stub implementation of app.
What you want to test is that the constructor registers listeners on specified routes + HTTP methods. I would advise putting Sinon.js stubs into your app stub, and then in your tests check that they are called with expected arguments.
I would use jasmine.createSpyObj to mock app (and maybe controller as well).
I think you just need to test that app.post gets called with the arguments '/api/' + resourceName and controller.create, because you aren't testing that express.post works correctly or not.
Here's how I'd do those two things specifically.
I'd modify router.js a little bit to make this easier:
var Router = function(app, resourceName, controller) {
app.post('/api/' + resourceName, controller.create.bind(controller))
}
module.exports = Router;
And then the test would look like this:
describe("Router", function() {
it("should route /api to controller.create", function() {
router = require('./router');
app = jasmine.createSpyObj('application', ['post']);
controller = jasmine.createSpyObj('controller', ['create']);
router(app, 'foo', controller);
expect(app.post).toHaveBeenCalledWith('/api/foo', jasmine.any(Function));
});
});
This isn't a perfect test because it isn't actually checking that controller.create specifically is getting called. That gets a little more complicated because of the .bind() stuff.
describe("Router", function() {
it("should route /api to controller.create", function() {
router = require('./router');
app = jasmine.createSpyObj('application', ['post']);
controller = jasmine.createSpyObj('controller', ['create']);
controller.create = jasmine.createSpyObj('binder', ['bind']);
controller.create.bind.and.returnValue('bar');
router(app, 'foo', controller);
expect(controller.create.bind).toHaveBeenCalledWith(controller);
expect(app.post).toHaveBeenCalledWith('/api/foo', controller.create.bind(controller));
});
});

Resources