In node.js(running this in Connect.js), I am able to set ether the Location or Set-Cookie with writehead, but not both at the same time. Currently, the below sets the cooke but the URL does not get redirected to the new location:
function foo(req, res, next) {
var url = /container-templates/;
if (url.test(req.url)) {
console.log(url.test(req.url));
console.log(req.url);
res.writeHead(302, ["Location", 'https://staging.dx.host.com' + req.url],
["Set-Cookie", "fake-token=5b49adaaf7687fa"]);
res.end();
} else { next() }
}
On a side note, I am doing this for the learning experience and do not want to use any pre-written plugins.
Response#writeHead expects the headers to be an Object not a list of array arguments.
The Node HTTP documentation has the following signature defined:
response.writeHead(statusCode, [reasonPhrase], [headers])
If you want to pass in multiple headers your code from above should read:
response.writeHead(302, {
Location: 'https://staging.dx.host.com' + req.url',
'Set-Cookie': 'fake-token=5b49adaaf7687fa'
});
The [] on the reasonPhrase means its optional and infers what you've provided to the function based on the types of the arguments.
Also, you don't need to wrap the key part of an object in quotes unless it contains characters that are invalid for variable names (like the -.) And a single ' will do for all strings -- there is no difference in javascript between ' and ".
Related
I am in the midst of writing a router that uses a JSON object as a manifest. The router uses the JSON manifest as a reference for where to route incoming requested url paths. The code below belongs to main.js that is ran using the node command to start the application. Near the top of the document, you can see the router object that takes the JSON Manifest as an argument. Below this bit of code, is another bit, that shows the router document where the router class is written into it, you can see there how I am attempting to route request using this JSON Manifest.
const app = require('./serv/katanaLib');
const Router = require('./serv/router');
var router = new Router.Router({
"^/(.*)$" : "/public/view/page/:[?]",
"^/user/(.*)$" : "/public/view/account/:[?]",
"^/style/(.*)$" : "/public/style/:[?]"
});
app.Server(8888, function(request, response)
{
const $_Request = app.parseHttpReq(request);
router.onReq($_Request);
});
Below is router.js, this is where I am having a problem, which I will explain at the bottem of this question.
module.exports.Router = class Router
{
constructor(JSON_manifest)
{
this.manifest = JSON_manifest;
this.keys = Object.keys(JSON_manifest);
this.values = Object.values(JSON_manifest);
};
onReq($_Request)
{
this.keys.forEach((key, i)=>
{
key = ('^' + key + '$');
var re = new RegExp(key, 'i');
let x = $_Request.path.search(re);
console.log('\n\n' + key);
console.log(re);
console.log(x + '\n\n');
});
};
};
As anyone can obviously see, I am writing Regular Expressions into the keys of the JSON manifest object. You can see that I search the requested path using a foreach loop, looping through each manifest key. The problem I am having is that my regex values are matching multiple times. When I run the code above, and make the follow request to my server.
http://localhost:8888/index
Request Recieved:
Parsed-Request:
{ path: '/index', method: 'GET', ext: '', type: 'text/html' }
^^/(.*)$$
/^^\/(.*)$$/i
0
^^/user/(.*)$$
/^^\/user\/(.*)$$/i
-1
^^/style/(.*)$$
/^^\/style\/(.*)$$/i
-1
that is a desireable result, but when I run:
http://localhost:8888/user/abc
i get...
Parsed-Request:
{ path: '/user/abc', method: 'GET', ext: '', type: 'text/html' }
^^/(.*)$$
/^^\/(.*)$$/i
0
^^/user/(.*)$$
/^^\/user\/(.*)$$/i
0
^^/style/(.*)$$
/^^\/style\/(.*)$$/i
-1
The Regular Expression is matching both, the first and second, which is undesirable. If I requested http://localhost/style/123 the requested path will match the first key and third key. If anyone has advice on how to write the regex so I can finish this that would be great, thank you...
To solve the issue with multiple regexes matching, it's best to test them in a specific order, from most specific to least specific. Then, the first match is to be used.
In your example, the regexes are provided as object keys.
Since object keys are unordered, you'd need to use an array instead to control the order of the regexes.
In this case, the order of regexes would be as follows (first and second can be swapped):
^/user/(.*)$
^/style/(.*)$
^/(.*)$
I am using Express with Body Parser. Given a below header key:
X-Master-Key
When I am using the below code snippet, it fails to output the value
req.headers['X-Master-Key'] // Fails
but when the above is changed to, it works
req.headers['x-master-key'] // Works
Further, when I tried to output req.headers, it turns out that Express outputs all the headers in a down-case format.
I started digging down further and tried using the below code, either of these snippets work
req.header('X-Master-Key'); // Works
// -- OR
req.header('x-master-key'); // Works
So what's the issue here? Why does Express changes all header keys to down-case? Moreover, how using req.header() is different from req.headers[]?
The problem arises because in the HTTP protocol, headers are case-insensitive. This means that content-type, Content-Type, and coNTEnt-tYPe all refer to the same header, and the Express framework needs to be able to handle any of them.
The different between req.headers (the object) and req.header (the function) is simply this:
If you want to get a property from a Javascript object, the property name is case-sensitive. So req.headers['content-type'] will work; req.headers['Content-Type'] will not. Why does the lower case version work? Because the Express framework, in an attempt to handle all the different possible cases (remember, HTTP will allow anything), converts everything to lower case.
But the developers of Express recognize that you (the developer) might be looking for Content-Type and you might not remember to convert to lower case, so they provided a function, req.header, which will take care of that for you.
So, in short:
This is recommended:
const myHeader = req.header('Content-Type');
Use whatever case you want - the function will convert it to lower case and look up the value in req.headers.
This is not recommended:
const myHeader = req.headers['Content-Type'];
If you don't use a lower-case header name, you won't get what you expect.
The problem comes down to case-sensitivity.
When you look at the documentation for req.get (which is aliased by req.header), it states:
Returns the specified HTTP request header field (case-insensitive match). The Referrer and Referer fields are interchangeable.
The w3 standard indicates that headers should be case-insensitive:
Each header field consists of a name followed by a colon (":") and the field value. Field names are case-insensitive.
So it would appear that node http module, which express uses, just treats them all as lower-case to "save you steps" according to this github issue
You can see that the express framework req object actually utilizes the node module http:
var accepts = require('accepts');
var deprecate = require('depd')('express');
var isIP = require('net').isIP;
var typeis = require('type-is');
var http = require('http');
var fresh = require('fresh');
var parseRange = require('range-parser');
var parse = require('parseurl');
Furthermore, in the code you can see that the req.header method converts whatever you give it to lower-case:
req.get =
req.header = function header(name) {
if (!name) {
throw new TypeError('name argument is required to req.get');
}
if (typeof name !== 'string') {
throw new TypeError('name must be a string to req.get');
}
var lc = name.toLowerCase();
switch (lc) {
case 'referer':
case 'referrer':
return this.headers.referrer
|| this.headers.referer;
default:
return this.headers[lc];
}
};
Finally, the http module parses headers using the matchKnownFields function which automatically lower-cases any and all headers that aren't "traditional headers", in which case it is case-insensitive.
Here is the responsible snippet, that implements the behavior you are seeing:
if (lowercased) {
return '\u0000' + field;
} else {
return matchKnownFields(field.toLowerCase(), true);
}
I want my API to support filtering on the different properties of my mongodb model. The brute force way of which I would use:
app.get('/api/thing/:id', thing.getThingById);
app.get('/api/thing/:name, thing.getThingByName);
app.get('/api/thing/:name/:color', thing.getThingByNameAndColor);
etc. This approach is obviously terrible. How can I add a single route to capture multiple params so that I can return things using something like
exports.getThingByParams = function (req, res, next) {
var query = thingModel.find (req.params);
query.exec (function (err, things) {
if (err) return next (err);
res.send ({
status: "200",
responseType: "array",
response: things
});
});
};
Use the URL query string. It's a set of name/value pairs invented for precisely this use case. It still works great after all these years despite current trends the everything must be in the path portion of the URL instead of the query string because ???. Look at your route - it even says "API" in it. It doesn't need to abuse the path to be "pretty" according to hipsters.
app.get('/api/thing', thing.search);
exports.search = function (req, res, next) {
//Remember any ID values need to be converted from strings to ObjectIDs,
//and there's probably additional sanitization/normalization to do here
var query = thingModel.find (req.query);
query.exec (function (err, things) {
if (err) return next (err);
res.send ({
status: "200",
responseType: "array",
response: things
});
});
};
Then the url to find a red thing named candy would look like
/api/thing?color=red&name=candy
Yup, you can. Just use the most explicit route example:
app.get('/api/thing/:name/:color', thing.getThingByProperty);
and inside getThingByProperty simply test for req.params.YourParam (name or color) and decide what to do. If color is requested but not name, you send in the url as follows:
/api/thing/-/red.
Very common sighting to have null params in routes expressed as dash.
OR, different approach, better pattern more API like (RESTFul) is:
/api/thing/:id that's by id and to offer pattern support
and for different property search use:
/api/things/search?property1=value&property2=value
That way you respect the collection pattern in your API (/api/things) and put the search in the query to keep it flexible. Test inside the search callback for req.query.property1 or property and act accordingly
Homework:
I know few things about url handling in node.js
app.param('id', /^\d+$/);
app.get('/user/:id', function(req, res){
res.send('user ' + req.params.id);
});
Will only accept /user/1, /user/2 ...i.e. with id as integer only
app.get('/:type(discussion|page)/:id', ...)
will only accept type with value of discussion or page
app.param('range', /^(\d+)\-(\d+)?$/);
app.get('/range/range=:range', function(req, res){
var range = req.params.range;
res.send('from ' + range[1] + ' to ' + range[2]);
});
will easily handle integer range and directly give us an array without any split or parsing and validation.
Question :
Normally server accept www.example.com/path and
www.example.com//path in same way but in node.js I have to write two
separate app.get check to do that. How can I achieve this by only
one check so that /path, //path, ///path gives same response
I have one url which looks for /:path and path can take values
listed in a dictionary
var dict={
"a":"You called a",
"b": "b is second",
"c": "cats are all over internet"
}
app.get('/:charc',function(req,res){
res.send(dict[charc]);
});
How can I restrict app to accept only a,b,c without putting an if else condition.
currently I am doing
if (typeof dict[charc] == 'undefined') res.send(404, 'Sorry, we cannot find that!');
Can I call range parameter(from homework part) sameway after '?' like
app.get('/range?range=:range',...
with url www.example.com/range?range=123-234
Regarding #1 if there's no better solution something like app.use(function (req,res,next) { req.url = req.url.replace(/[/]+/g, '/'); next(); }); would make sure no subsequent routes would see any duplicate slashes. -#Andreas Hultgren
Regarding #3, I'm pretty sure it's not possible to match query strings with express' router. Can't find a source that confirms this though. -#Andreas Hultgren
Followup to #Gaurav's comment for #1: Instead of changing the url in the route and continuing to handle the request, it ought to trigger a 3xx redirect so that the offending request is forwarded to the canonical variant. (This way the proper URL is updated in the browser. Or whatever type of client making the request is made aware that the original double-slash URL is technically invalid.)
In the following Express function:
app.get('/user/:id', function(req, res){
res.send('user' + req.params.id);
});
What are req and res? What do they stand for, what do they mean, and what do they do?
Thanks!
req is an object containing information about the HTTP request that raised the event. In response to req, you use res to send back the desired HTTP response.
Those parameters can be named anything. You could change that code to this if it's more clear:
app.get('/user/:id', function(request, response){
response.send('user ' + request.params.id);
});
Edit:
Say you have this method:
app.get('/people.json', function(request, response) { });
The request will be an object with properties like these (just to name a few):
request.url, which will be "/people.json" when this particular action is triggered
request.method, which will be "GET" in this case, hence the app.get() call.
An array of HTTP headers in request.headers, containing items like request.headers.accept, which you can use to determine what kind of browser made the request, what sort of responses it can handle, whether or not it's able to understand HTTP compression, etc.
An array of query string parameters if there were any, in request.query (e.g. /people.json?foo=bar would result in request.query.foo containing the string "bar").
To respond to that request, you use the response object to build your response. To expand on the people.json example:
app.get('/people.json', function(request, response) {
// We want to set the content-type header so that the browser understands
// the content of the response.
response.contentType('application/json');
// Normally, the data is fetched from a database, but we can cheat:
var people = [
{ name: 'Dave', location: 'Atlanta' },
{ name: 'Santa Claus', location: 'North Pole' },
{ name: 'Man in the Moon', location: 'The Moon' }
];
// Since the request is for a JSON representation of the people, we
// should JSON serialize them. The built-in JSON.stringify() function
// does that.
var peopleJSON = JSON.stringify(people);
// Now, we can use the response object's send method to push that string
// of people JSON back to the browser in response to this request:
response.send(peopleJSON);
});
I noticed one error in Dave Ward's answer (perhaps a recent change?):
The query string paramaters are in request.query, not request.params. (See https://stackoverflow.com/a/6913287/166530 )
request.params by default is filled with the value of any "component matches" in routes, i.e.
app.get('/user/:id', function(request, response){
response.send('user ' + request.params.id);
});
and, if you have configured express to use its bodyparser (app.use(express.bodyParser());) also with POST'ed formdata. (See How to retrieve POST query parameters? )
Request and response.
To understand the req, try out console.log(req);.