Is it possible to access the NancyContext from a route definition?
What I would like to build is a single route for a legacy request that receives parameters in a way like this:
/points/upload?longitude=12.34&latitude=23.45
(And I did not find a way to specify these parameters with patterns, as Nancy seems to ignore anything behind the question mark.)
Everything after the question-mark is a query-string and you access those using the Request.Query property, inside your route
Get["/points/upload"] = x => {
var long = Request.Query.longitude;
var lang = Request.Query.latitude;
return 200;
}
Related
The code segment is:
app.get('/api/photo/:S3ObjectKey', photo.get);
And photo.get():
const S3ObjectKey = req.params.S3ObjectKey;
if (!S3ObjectKey) {
console.log("No S3ObjectKey specified");
}
console.log("S3ObjectKey: ", S3ObjectKey);
const canShow = true;
if (canShow) {
const bucket_name = process.env.AWS_S3_BUCKET_NAME;
const data = await S3.getFile(bucket_name, S3ObjectKey);
data.Body.pipe(res);
}
Is there a way to map all the rest of url as one parameter, such that:
a GET request to https://mylovelydomain/api/photo/bucketMyPhoto/2020/07/12/home/table.jpeg would hit the api endpoint and S3ObjectKey has the value bucketMyPhoto/2020/07/12/home/table.jpeg?
Express uses path-to-regex:
Custom Matching Parameters
Parameters can have a custom regexp, which overrides the default match ([^/]+).
On your routing path you can use * to get everything overwriting that default match (note that this code also reatcs to empty requests):
app.get("/api/photos/:S3ObjectKey(*)", /*...*/)
As per express documentation, route path supports regular expression, so should be able to do it. Quoting the example from the documentation.
I have a custom logging function which is assigned to express requests req.log object using middleware.
The purpose of this is for the logging funtion to be able to read the request headers.traceid, before transmitting the log event to a seperate service.
This is working perfectly fine using middlware or with an extra parameter in the function, however to simplify the use of it.
What I'd really like to know if there's a way for the function to be able to read the req object from the scope it was called in, without referencing it either using middlware or as a function paramter?
// log.js
module.exports = () => {
console.log(...arguments)
const req = getVarFromParentScope("req") || undefined
const traceId = req?.headers.traceid || null
// POST { timestamp, traceId, args: { ...arguments } } to logging service
}
No, it isn't.
(And if it was, then the ability for a function to access a variable inside another function just because it was the calling function would make it very easy to write code that was very hard to debug.)
I'm trying to make a simple HTTP module, similar to express, to help learn how to use HTTP.
When working with express I often use parameters such as:
app.get('/id/:id' (req, res) => {console.log(req.params.id); stuff})
I was wondering-
Is it possible just using HTTP?
If it isn't able to be done with just HTTP, then how would you go about creating it?
Your question is a little bit confusing but I think what you mean is how to implement an HTTP Router in pure javascript instead of relying on a framework like express.
If that's the case I support your initiative 100%! It's really important to understand what is going on behind the scenes.
A good way to really understand a good way to do so would be by reading the source code of a good router that is already out there.
You could study the Express router's source code but I recommend you to go play with find-my-way which is a dedicated router that you can use with HTTP only without any other framework.
Yes but you need to process it manually.
Assuming you're using node's http module, the information you are looking for is in req.url but it includes the whole url path.
For example, you want to parse /id/:id, and the browser is making a request to http://your.server/id/100?something=something. Then the value of req.url will be:
/id/100?something=something
But if you are writing an HTTP module from scratch using the net module then you need to know the format of an HTTP request. Basically an HTTP request looks like a text file with the following format:
GET /id/100?something=something HTTP/1.1
Host: your.server
The header section ends with double newlines. Technically it should be \r\n\r\n but \n\n is also acceptable. You first need to get the url from the first line of the request between the protocol (GET in the example above but can be POST, PUT etc.) and the HTTP version number.
For anyone interested in writing a HTTP server from scratch I always recommend James Marshall's excellent article: https://www.jmarshall.com/easy/http/. It was originally written in the late 90s but to this day I haven't found a clearer summary of the HTTP protocol. I've used it myself to write my first HTTP server.
Now you have to write code to extract the 100 from the string.
If you are doing this manually, not trying to write a framework like Express, you can do it like this:
const matches = req.url.match(/id\/([^\/?]+)/);
const id = matches[1];
If you want to write a library to interpret the /id/:id pattern you can maybe do something like this (note: not an optimized implementation, also it behaves slightly differently to express):
function matchRoute (req, route) {
const [ urlpath, query ] = req.url.split('?'); // separate path and query
const pathParts = urlpath.split('/');
const routeParts = urlpath.split('/');
let params = {};
for (let i=0; i<pathParts.length; i++) {
if (routeParts[i].match(/^:/)) { // if route part starts with ":"
// this is a parameter:
params[routeParts[i].replace(':','')] = pathParts[i];
}
else {
if (routeParts[i] !== urlParts[i]) {
return false; // false means path does not match route
}
}
// if we get here (don't return early) it means
// the route matches the path, copy all found params to req.params:
req.params = params;
return true; // true signifies a match so we should process this route
}
}
// Test:
let req.url = '/id/100';
let result = matchRoute(req, '/id/:id');
console.log(result, req.params); // should print "true, {id:100}"
There is a generic structure of nodejs callback functions :
function(req,res){
//handle callback
}
I just want, callback should work correctly even if sometimes i write in mistake (res, req)
Given mixture of req or res, how do i find which one is actually request and which one is response.
req is an IncomingMessage object and res is a ServerResponse object.
So check for unique properties on each, for example if the particular object has a writeHead() function, then it's the response object.
You may also be able to use instanceof to check: res instanceof http.ServerResponse.
Functions in JavaScript are not programmatically prototyped by parameter names. The length property of a function only provides the number of parameters specified in the definition:
var fn = function (one,two,three) { return "x"; };
console.log(fn.length); // 3
Although there are ways to retrieve these names (see this question), usually procedures simply ignore how you name the parameters of your functions/closures, and instead assume that you are following the proposed API.
For this reason, it remains as the best practice to pay attention to the API and name parameters accordingly. In a Node.js HTTP request listener, the request comes always before the response (it is documented and many examples are available). As mentioned by other answers, you can dynamically check whether the request is an http.IncomingMessage or whether the response is an http.ServerResponse, but it seems to me that you can avoid introducing an overhead just with proper naming.
With that said, given the variables req and res, it is easy to make a check at the top of a function body, like the code below. However, do note that this would only be remedying what can be prevented by just following the API contracts, and as thus I cannot recommend it (unless you really want to make functions with a more flexible API).
function(res,req) {
if (req instanceof http.ServerResponse) {
// wrong order, swap.
var t = req;
req = res;
res = t;
}
// handle request
}
Is it possible with expressjs to have multiple routes calling the same resource, something like that:
app.get('/users/:user_id', users.getOne)
app.get('/users/:username', users.getOne)
I would like to be able to call users.getOne whichever params (:user_id or :username) is used in the get request.
In the users.getOne function, how can I determine wich one was used and build my query according to it?
exports.getOne = function(req, res){
var queryParams = ? // I need help here
Users
.find(queryParams)
...
Thanks!
Possibly related: express.js - single routing handler for multiple routes in a single line
From express's view, both of those routes will match the same set of request URLs. You only need one of them and you can name it to make more sense:
app.get('/users/:key', users.getOne);
//...
// http://stackoverflow.com/a/20988824/266795
var OBJECT_ID_RE = /^[a-f\d]{24}$/i;
exports.getOne = function(req, res) {
var conditions = {_id: req.params.key};
if (!OBJECT_ID_RE.test(req.params.key)) {
conditions = {username: req.params.key};
}
Users.find(conditions)...
If you end up wanting this pattern in many routes throughout your code base, you can extract it into a /users/:user param and use app.param as per #alex's answer, but encapsulate the code to locate the user and stick it on to req.user so the actual route handler can just assume the user has been properly found and loaded by the time it executes, and 404 handling can be centralized as well.
Those are in fact, from express's view, the same route.
No, they are not. One route has :user_id parameter, another one has :username.
This would be a proper solution:
var OBJECT_ID_RE = /^[a-f\d]{24}$/i;
app.param('user_id', function(req, res, next, value, name) {
if (OBJECT_ID_RE.test(value)) {
next()
} else {
next('route')
}
})
app.get('/users/:user_id', users.getOne)
app.get('/users/:username', users.getOne)
app.param set the prerequisite for the route to be called. This way when user_id matches a pattern, first route gets called, otherwise second one.