I'm hoping to better understand the difference between express's app.get() and app.use().
I understand that app.use applies to all HTTP verbs.
I have also read that "app.use() adds middleware rather than a route"
I'd like to understand why this fact causes this behaviour...
I have an express API server that needs to proxy a React development web server.
This means that all routes that are not API routes have to be proxied.
When I proxy the routes like this, it works:
var proxy = require('express-http-proxy');
module.exports = function set_react_catchall_routes(app) {
/* Final route to send anything else to react server. */
app.get('*', proxy('localhost:3000'));
app.post('*', proxy('localhost:3000'));
}
But when I do this it does not work:
app.use('*', proxy('localhost:3000'));
Specifically, the "index" page is proxied and served up, with content like this:
<body>
<div id="root"></div>
<script type="text/javascript" src="/static/js/bundle.js"></script>
</body>
and the client requests the javascript react bundle, but then "nothing happens".
I'm reasonably sure that there aren't "other" HTTP requests involved when it does work (other than GET and POST) because none are logged.
So what would be the difference?
Try putting this logging at the top, it should help to clarify what's going on:
app.use(function(req, res, next) {
// req.path will be '/static/js/bundle.js'
console.log('path1: ' + req.path);
next();
});
app.use('*', function(req, res, next) {
// req.path will be '/'
console.log('path2: ' + req.path);
next();
});
app.all('*', function(req, res, next) {
// req.path will be '/static/js/bundle.js'
console.log('path3: ' + req.path);
next();
});
When you use app.use it will strip off the matching section of req.path. If you don't specify a path (logging section 1) it won't strip anything off. Similarly section 3 is using app.all (app.get, etc. all work the same way) which doesn't change req.path either. It's section 2 that's the kicker.
To understand why this happens consider this example:
var router = express.Router();
router.get('/profile', ...);
app.use('/user', router);
When a request comes in for /user/profile the app.use will strip off the /user part of the path. As far as router is concerned the path is just /profile.
To quote the docs, http://expressjs.com/en/4x/api.html#req.path
When called from a middleware, the mount point is not included in req.path.
The path for a call to app.use is a bit like a 'starts with' and anything that matches is thrown away. For * that matches everything so it throws away everything.
If you take a quick dive into the source code for express-http-proxy you'll see that it uses req.path to determine the path of the proxy request. If you just use app.use without a path it should work fine.
There are some other request properties that are similarly relevant to understanding app.use:
req.url is similar to req.path but with the query string included. Just like req.path it will have the section matching the mountpath removed by app.use. Note that the Express Request inherits the url property from Node's http.IncomingMessage so it isn't explicitly listed in the Express documentation.
req.originalUrl starts off the same as req.url but it will not be changed by app.use.
req.baseUrl is used to store the section of the path that has been removed by app.use.
See the documentation for req.originalUrl for more details on all three of these properties.
Related
So, I want to add a ads.js to my site, but the example from Next github is not so clear. Anyone knows how to do it?
Here's the next example with robots and a sitemap:
https://github.com/zeit/next.js/tree/canary/examples/with-sitemap-and-robots-express-server
Thank you!
Depends on where is this file needs to go you can do one of this options.
if not matter, just drop it inside the /static folder and call it whit go.to/static/ads.txt
drop it inside /static folder and add this script to your custom server to handle this new path /ads.txt in the way to make accessible with go.to/ads.txt
server.get('/ads.txt', (req, res) => {
const filePath = join(__dirname, 'static/ads.txt')
app.serveStatic(req, res, filePath)
})
server.get('*', (req, res) => handle(req, res))
this solution work in the express server.
Just add your ads.txt file inside the /public folder.
Assuming you've setup a static directory in node & using express:
app.use(express.static(__dirname + '/public'));
Simply place the 'ads.txt' file in the 'public' directory and it will automatically be served / available via a get request to yourapp.com/ads.txt
when I use postman to test my backend api, as i was trying to get a specific id, only when I put the colons before the id, the route direct to "/properties".when add the id right after the /, there come out the 404 error. But I already put the colons in the route, which I think is the right way to claim a param as the express doc says. I don't know why the app cant find the right route, and where the problem might be.
propertyRouter.route('/:id')
.get(function (req, res, next) {
Property.findById(req.params.id)
.exec(function (err, property) {
if (err) throw err;
});
res.json(property);
});
})
(Source)
I'm having much trouble getting OAuth2 to work with a generic OAuth2 provider. Here's the situation.
A service provides an OAuth2 authentication method to where I want to authorize with. I've created an AngularJS app that has the following configuration for satellizer:
authProvider.baseUrl = 'http://localhost:3030/user/authorize';
$authProvider.oauth2({
name: 'customname',
url: '/token',
clientId: 'someapp',
requiredUrlParams: ['scope'],
scope: ['profile'],
authorizationEndpoint: 'http://location.to.oathserver',
redirectUri: 'http://localhost:3000'
});
The baseUrl points to my node server that should handle the middleware part.
I've also the following code that triggers the authentication part.
$scope.authenticate = function(provider) {
$auth.authenticate(provider)
.then(function(response) {
console.log(response);
})
.catch(function() {
//something went wrong
});
}
So far this all seems to work great and looks very similar to what is documented by Satellizer! Now once I start the angular app and start the authentication I see requests coming by that target my Node service.
Next I've my node.js service that hooks to the 'user/authorize/token' URL. Here's the code:
router.options('/authorize/token', function(req, res, next) {
//var token = req.header('Authorization').split(' ')[1];
res.end();
});
and:
router.post('/authorize/token', function(req, res, next) {
var authCode = req.param('code');
var cliendId = req.param('clientId');
var payload = jwt.decode(authCode, 'mySecret');
});
Here's where it all seems to go wrong. First I seem to get an OPTIONS request. I've not really an idea what to do with it as I can't seem to find anything in the documentation about an OPTIONS request. I thought it would might contain the 'Authorization' header but that doesn't seem the case so I close the connection with a res.end();
I also inspected the request in Chrome but I can't seem to find a header that has this exact name.
Next I get a POST request. This does seem to contain some things, hooray! I get the following object:
{
code: "ZFFeat9pWfHzw4rGmjFYwucPRMFnBOkd2odEObvo",
cliendId: "someapp",
redirectiUri: "http://localhost:3000"
}
This looks to me like the authorization code that I should have to decode. That's what you see me trying as well in the code above. Unfortunately this seems to throw me an error
Error: Not enough or too many segments
This tells me I'm doing probably something wrong, and I got stuck.
I do have some PHP code that seems to work for someone else but I don't fully understand and can't really relate the code to my code since PHP is not my speciality and node.js/JavaScript not his. So here goes the PHP code:
handle_cors(); // Handle CORS for cross domain requests
// Get JSON data
$input = json_decode(file_get_contents("php://input"), true);
// Create Provider
$provider = new SomeApp\OAuth2\Client\Provider\SomeApp([
'clientId' => 'someapp',
'clientSecret' => 'mySecret',
'redirectUri' => $input['redirectUri'],
]);
// Optional: Now you have a token you can look up a users profile data
try {
// Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $input['code']
]);
// We got an access token, let's now get the user's details
$user = $provider->getResourceOwner($token);
header('Content-Type: application/json');
$result = $user->toArray();
$result['token'] = create_token('my-example-key', $user->getId());
echo json_encode($result);
exit();
} catch (Exception $e) {
// Failed to get user details
exit('Oh dear...' . $e->getMessage());
}
Hopefully someone can help me out! Thanks in advance.
Sorry guys, I've been able to solve it myself. I found out that I was missing some URL's to POST to and GET from. After that the examples from Satellizer became clear and was able to use them almost as a carbon copy.
In node this is how I define my details route (render as jade and send).
app.get('/details', function(req, res){
jade.renderFile('details.jade', function(err, html){
if(err){
console.log(err);
}
res.send(html);
});
});
In jade with 'blah' is clicked then calls navigateToPath function with params.
a(ng-click="navigateToPath(date_obj_key, part)") blah
In angular, this function should go to this path. The url path changes in the browser but it does not REDIRECT to the page. Help (yes I am injecting the location service)
$scope.navigateToPath = function(date, part){
var path = '/details?date='+date+'&part_type='+part;
$location.path('/details').replace(); //also tried $location.url(path)
$scope.apply(); //also tried $scope.$apply and $rootScope.$apply as func
console.log($location.path());
}
I am using Fire Fox developer tools(F12) and put a break point on where I used $window.location in my project and looked at the values in $window.location and this what it shows:
It seems like this would work. for both a location in the same folder or sub-folder as well as going to a completely different web site.
$window.location = $window.location.origin + path
or
$window.location = <whatever website you want to go to>
In my case I was just using the $window.location to call a rest service to download a file that the user selected from a ui-grid while still staying on the same page. and this may have worked for me because my scenario is a bit different then what yours is I think. so all I had to do was
$window.location = "../../services" + "<path to my rest service>" + $scope.shortCode + "/" + $scope.wireInstSelectedRow.entity.fileName;
#shapiro I am not sure why this does not work
$location.path('/details').replace();
I tried the same thing originally in my project and based on the documentation: https://docs.angularjs.org/api/ng/service/$location It seems like that would work and what its supposed to be used for but what I noticed is it would put a '#' character in the url before it put the path I wanted it to take which was preventing it from going to that page. Anyhow for me it seems like as long as you are going to an html page that is in the same folder or sub-folder just doing
$window.location = <the path you want to go to>;
is a good solution... at least it did the trick for me. Hope this helps.
I don't have a problem as such as I have a work-around. However the process of understanding and developing the work-around has highlighted what seems to me a fundamental gap in my knowledge (as a self-taught programmer, this happens a lot ;-) !) and I can't seem to find the answer to plug it anywhere.
Standard node.js/express setup with for example:
app.get('/index.htm', function (request, response) {
console.log("/index.htm");
});
app.get('*', function (request, response) {
console.log("*");
});
Why when the user connects directly to index.htm (by typing in the URL although I've not tried clicking on a link) does nothing fire ? The page is served no problem. The obvious use-case for this is to check if the user is authenticated prior to serving the static page.
FYI the workaround is to pass the user to a route such as "/authenticated" (which matches fine), do the check and then redirect and basically block everything else.
Thanks for educating me in advance ;-)
N
UPDATE to Raynos' question - yes (ignore the server/app variable name discrepancy).
// CREATE SERVER
var server = express.createServer();
server.configure(function() {
server.use(express.static(__dirname + '/public'));
server.use(express.logger());
server.use(express.errorHandler({dumpExceptions: true, showStack: true}));
// start of session stuff
server.use(express.cookieParser());
server.use(express.session({store: sessionStore, secret: 'secret', key: 'express.sid'}));
});
I've now found that the routing matches fine once you delete the static file i.e. in the original example, deleting index.htm means that the console will now log "/index.htm".
N
Just to bring some closure to this question:
Express static handler if it finds the file it will serve it and it won't let the other handlers to be called. If you remove it, then app.get('/index.htm' handler will be called. If you want to have a callback on all URLs just add server.use(function(req, res, next) {...}) before everything and that function will be called on all routers, make sure you call next if you want other middlewares or route handlers to be called.