I've got some troubles when start working with Next.js
Here is the deal. I have multiple filters, depends on them I make up URL. All of this URL's for one page.
It can be like:
/
/one
/one/two
/one/two/three
This nested is required. How can I create one handler for catch any of these URL's?
I use Express like this, but it doesn't help.
server.get('/*', async (req, res, next) => {
try {
app.render(req, res, '/')
} catch (e) {
next(e)
}
})
Thanks!
Use Dynamic routing like that :
pages/[one]/[two]/[three].js
cf:
Multiple dynamic route segments work the same way.
For example, pages/post/[pid]/[comment].js would match /post/1/a-comment. Its query object would be: { pid: '1', comment: 'a-comment' }.
https://nextjs.org/docs#dynamic-routing
Hope it's help.
New feature introduced in Next.js 9.5
Solution: Rewrites
See also: Redirects & Headers:
Announcing comment: https://github.com/vercel/next.js/discussions/9081#discussioncomment-48301
Besides that I am aware of some wildcard matching that nests paths deep in the default routing, but I feel Rewrites is a better solution in most use cases.
use npm next-routes in order to use path patterns (regex) to bind multiple urls to a page
Related
Is there any way I can trigger a policy on a specific request method (e.g. DELETE) rather than on specific routes?
I'd imagine something like this:
module.exports.policies = {
'DELETE *': 'isAdmin'
}
My goal here is to expose the blueprint api to admins only, so that I can keep it in production, as it's a very useful tool for allowing third party scripts to add extra functionality.
I'm on Sails 1.0 right now.
One way to do that might be to add the check for the request method to the actual admin policy, however that doesn't quite seem like the best solution to me.
You can override the blueprint for all models for a particular method. You can do this for DELETE by creating a file destroy.js in /api/blueprints/ and then adding your code for what you want to do when a DELETE comes through:
module.exports = function(req,res, next) {
if(ACLService.hasPermission(req.user.acl, 'admin')) {
//Ok to allow delete here
} else {
return res.unauthorized();
}
};
This is how I've done it in the past, but looking at the docs for the just released SailsJS 1.0:
https://sailsjs.com/documentation/reference/blueprint-api
You may need to add this hook for overriding blueprints in 1.0
https://www.npmjs.com/package/sails-hook-custom-blueprints
Here is one method that you can use, I am not claiming that it is the right way, but you can consider it:
You can write your own hook. How to do this: https://sailsjs.com/documentation/concepts/extending-sails/hooks/project-hooks
Basically here is the solution with a hook:
1 Create a hooks folder under your api folder.
2 In the hooks folder create another folder - the name will be the name of your hook (say my-hook).
3 In api/hooks/my-hook create a file index.js and in it put the following code:
module.exports = function myHook(sails) {
return {
routes: {
before: {
'/*': function (req, res, next) {
if (req.method.toUpperCase() === 'DELETE') {
return sails.hooks.policies.middleware.isadmin(req, res, next); // note - your policy function name must be called here with all lowercase, otherwise it will not work.
}
return next();
}
}
}
};
};
Then in your isAdmin.js policy you can check if your user is an admin and if not:
return res.forbidden();
if it is admin:
return next();
In my sails.js application i have two routes like this:
'/': {controller:'HomeController',action:'home'},
'GET /:category/:subcategory/:keyword':{controller:'SearchController',action:'index'
When I run the default route (/) it will always execute this route
GET /:category/:subcategory/:keyword .
Why is this happening??
The order of routes in route file is
1) /
2) GET /:category/:subcategory/:keyword
As mentioned in the comment above, your very general route /:category/:subcategory/:keyword is being hit because it must match asset urls on your homepage. This route will match any three-part path, ex:
/images/icons/smiley.png
/scripts/thirdparty/jquery.min.js
Etc!
There would be two approaches to fix this. One would be making your SearchController urls more specific. Maybe /search/:category/:subcategory/:keyword would be a good idea? This is the simplest and should clear up any conflicts with your assets right away.
But if you really need catch-all routes that can interfere with other specific routes, then the solution is to catch the specific routes first. For example, in routes.js:
'GET /images/*': 'RouteController.showAsset',
'GET /scripts/*': 'RouteController.showAsset',
'GET /styles/*': 'RouteController.showAsset',
//...
'GET /:category/:subcategory/:keyword': 'SearchController.index',
Then create a controller RouteController with the method:
showAsset: function(req, res) {
var pathToAsset = require('path').resolve('.tmp/public', req.path);
// ex should be '.tmp/public/images/icons/smiley.png'
return res.sendfile(pathToAsset);
},
You may need to add something in to check for file existence first, but this is the idea.
I found this approach worthwhile when I wanted a /:userName route that would not conflict with all of my /contact, /about, /robots.txt, /favicon.ico, etc. However, it takes work to maintain, so if you think the first approach can work for you, I would use that.
I am about to redo code switching from Angular 1 to React. I have been using React Boilerplate for a while, which uses React Router v3, and I really like the setup. My Angular project uses URLs like example.com/#/about and I really don't like the concept of the # sign so I want to make the URL look like example.com/about.
The problem is some URLs have been given out to the public so I would like for them to be backward-compatible. For example, if a user goes to /#/about, then they will be automatically redirected to /about.
If you know the React Boilerplate's ecosystem, that would be helpful. I know the concept of redirecting is simple, but I would like to do this in a clean way within the boilerplate. Any ideas?
Thanks!
Thanks to #Calvin's comment above, it sent me down a different thought process. I found out that React Boilerplate allows redirects like so:
onEnter: (_nextState, replace, next) {
replace('/something');
next();
}
This is to be placed inside of the routes.js file. The link to the question/answer where I found this answer is here.
Edit
The exact answer ended up being this function:
const sniffHash = (nextState, replace, next) => {
if (nextState.location.hash !== '') {
let path = nextState.location.hash.replace('#', '');
replace(path);
}
next();
}
in which I used the variable name on my home route like so:
path: '/',
name: 'home',
onEnter: sniffHash,
getComponent(nextState, cb) {
//React Boilerplate router stuff
}
The only caveat is if I needed anchor links, then I would have to figure something out. Not ideal, but it may help someone who has to make this type of transition.
With the following route:
app.get('/', controller.web.Home);
How would I add within '/' something which would allow a match for /, /index and /index.html? I would also like to use this approach for all other routes so that users don't see an error page when adding .html to a path.
I have seen this mentioned on the Express website, however there are no clear explanations for matching multiples. Thanks in advance.
Express uses path-to-regex for routing strings meaning you can use regular expressions or string patterns to match routes.
How would I add within '/' something which would allow a match for /, /index and /index.html
Something like this would work:
app.get('/|index|index.html', controller.web.Home);
I would also like to use this approach for all other routes so that users don't see an error page when adding .html to a path.
You can also write a small helper function that takes care of this for any route:
function htmlExt(route) {
return route + '|' + route + '.html';
}
And the use it for any route:
app.get(htmlExt('index'), controller.web.Home);
app.get(htmlExt('blog'), controller.web.Blog);
// ...
Other approaches
You can also pass in an array of paths instead so this should also work:
function htmlExt(route) {
return [route, route + '.html'];
}
app.get(htmlExt('index'), controller.web.Home);
Another way would be to use a regex. Perhaps one that accepts a route and an optional .html extension:
app.get(/index(.html)?/, controller.web.Home);
You can find other useful examples in Express Routing docs.
You can define an array of paths as the first argument:
app.get(['/', '/index' , '/index.html'], controller.web.Home);
using express 4.x
app.get('/(index*)?', controller.web.Home);
reference
http://expressjs.com/en/guide/routing.html
If you want a global approach you can use a middleware function. Put it before all of your routes.
app.use(function(req, res, next) {
var match = req.path.match(/(.*)\.html$/)
if (match !== null) {
res.redirect(match[1]);
} else {
next();
}
});
It redirects every path ending with .html to a route without this extension.
Of course the route path '/' needs to be handled separately.
I'm just getting started with express.js and am failing to understand how one defines discrete "pages" (in the traditional sense) that one can link to internally.
I'm using Jade as a template engine and I see how it pulls the various components together and references them in the app.js (which is what is initially invoked by npm) so that, in effect is my "index". Would be great to see a tutorial on what one does to then build out pageA, pageB, pageC so that they can be linked to via <a href="pageA.html"> (or the Jade equivalent).
I'm assuming this is possible, right?
Express.js itself does not offer URL generation, only a built-in router.
You would need to use an additional package to perform URL generation, or build it yourself. Maybe you find something fitting in this question's answers: URL generation for routes in express
If you do not care about route generation and want to "hard code" the URLs, you would need to add a route for each static page, like this:
// routes.js
app.get("/pageA.html", function(req, res, next) { res.render("static/page_a", { templateLocals: "here" }) };
app.get("/pageB.html", function(req, res, next) { res.render("static/page_b") };
Or, if you have many of those pages, you could use a controller for this:
// static_page_controller.js
module.exports = function(pageTemplate) {
return function(req, res, next) {
return res.render("static/" + pageTemplate);
}
}
And use it like this:
// routes.js
var staticController = require("./static_page_controller");
app.get("/pageA.html", staticController("page_a"));