How can I create an Express route that handles URLs that look like this? - regex

app.get('/:service[SOMETHING GOES HERE]', function(req, res, next){
console.log('Service is:', req.params.service);
});
This needs to catch URLs that can look like any one of:
/foo
/foo/bar
/foo/bar/baz
The call back isn't concerned with anything that comes after foo, but ideally should be able to access foo as a parameter called service without having to manually parse the path.
I've been using this to test and still haven't found anything that does exactly that. Closest so far is /:service*.
Edit: No it's not a duplicate of the one where the answer is /:service/* because that doesn't cover /foo.

Using /:service* in actual Express routes does exactly what you want:
/foo maps to { '0': '', service: 'foo' }
/foo/bar maps to { '0': '/bar', service: 'foo' }
/foo/bar/blah maps to { '0': '/bar/blah', service: 'foo' }
The Express Route Tester, for some reason, maps these URL's differently for that these kinds of patterns (it might be configured differently than Express).

You can use the app.use() function for that. Read the doc about path handling for more info. Your code once modified will be:
app.use('/foo', function(req, res, next){
console.log('Service is:', req.params.service);
});
The downside is that you are not going to recover foo as the service parameter.

Related

How to determine the Ember route name from a URL?

I need determine the route name from a URL string.
This is normally work that the Router does internally, but because of {insert long story here} I need to do it manually. I am given a piece of data from the API that looks like 'gallery/123' and I need to know the route name is 'gallery.post'. So that I can do route.replaceWith(determinedRouteName);
Need to turn 'gallery/123' into 'gallery.post'
Need to turn 'stuff/99/comments' into 'stuff.post.comments'
Etc
Ember relies internally to the router-recognizer micro lib. I think that your best option is to use it as well.
Unfortunately, accessing the router instance is currently requiring to rely on the private -routing service. (Note: here is a pending RFC about offering a public router service).
Injecting this private service:
import Ember from 'ember';
export default Ember.Route.extend({
router: Ember.inject.service('-routing'),
...
});
The recognize function returns the list of all handlers that you can join if you want to build the complete string or anything else.
Ex:
this.router.recognize("stuffs/99/comments");
> [{handler: "application", ...},
handler: "stuffs", ...},
handler: "stuff", ...},
handler: "comments", ...},
handler: "comments.index, ...}]
Hope it helps
Are you going to always only have one ID in the route? Or will the first ID always be the 'post' ID at least? If so you this isn't really an Ember issue it would just be a JavaScript find/replace that always follows the same format assuming the API data always come back in the same format.
Assuming the API always provides this format:
"something/:post_id/optionally_something_else"
You could take the string that the API provided and run it through a couple replace methods:
var apiString = "stuff/99/comments";
apiString = apiString.replace(/[0-9]+/, "post").replace(/\//g, "."); // => "stuff.post.comments"
// Also works with:
var apiString = "gallery/123";
apiString = apiString.replace(/[0-9]+/, "post").replace(/\//g, "."); // => "gallery.post"
DISCLAIMER: This is pretty icky and will break if the string doesn't follow this format. It will also only covert the first set of digits to "post". But as usual, use it if you must :P

Ember makes unwanted call to backend in model hook

I want to be able to retrieve a certain conversation when its id is entered in the URL. If the conversation does not exist, I want to display an alert message with a record not found.
here is my model hook :
model: function(params){
return this.store.filter('conversation', { status : params.status}, function(rec){
if(params.status == 'all'){
return ((rec.get('status') === 'opened' || rec.get('status') === 'closed'));
}
else{
return (rec.get('status') === params.status); <--- Problem is here
}
});
}
For example, if I want to access a certain conversation directly, I could do :
dev.rails.local:3000/conversations/email.l#email.com#/convid
The problem is when I enter a conversation id which doesn't exist (like asdfasdf), ember makes call to an inexisting backend route.
It makes a call to GET conversation/asdfasdf. I'm about sure that it is only due to the record not existing. I have nested resources in my router so I'm also about sure that it tries to retrieve the conversation with a non existing id.
Basically, I want to verify the existence of the conversation before returning something from my hook. Keep in mind that my model hook is pretty much set and won't change, except for adding a validation on the existence of the conversation with the id in the url. The reason behind this is that the project is almost complete and everything is based on this hook.
Here is my router (some people are going to tell me you can't use nested resources, but I'm doing it and it is gonna stay like that so I have to work with it because I'm working on a project and I have to integrate ember in this section only and I have to use this setup) :
App.Router.map(function(){
// Routing list to raw namespace path
this.resource('conversations', { path : '/' }, function() {
this.resource('conversation', { path : '/:conversation_id'});
});
});
This also happens when I dont specify any id and I use the hashtag in my url like this :
dev.rails.local:3000/conversations/email.l#email.com#/ would make a call to conversation/
I know it is because of my nested resource. How can I do it?
By passing a query to filter (your { status : params.status}) you are asking Ember Data to do a server query. Try removing it.
From the docs at http://emberjs.com/api/data/classes/DS.Store.html#method_filter:
Optionally you can pass a query, which is the equivalent of calling find with that same query, to fetch additional records from the server. The results returned by the server could then appear in the filter if they match the filter function.
So, remove the query:
model: function(params){
return this.store.filter('conversation', function(rec) {
if (params.status == 'all') {
return rec.get('status') === 'opened' || rec.get('status') === 'closed';
} else {
return rec.get('status') === params.status;
}
});
}
Ok so here is what I did. I removed my nested resource because I realised I wasn't using it for any good reason other than redirecting my url. I decided to manually redirect my url using javascript window.location.
This removed the unwanted call (which was caused by the nested resource).
Thanks to torazaburo, you opened my eyes on many things.

node.js - sending regex to server

I've created a client side mongodb interface to talk to server side mongodb.
it's very similar to the mini-mongo implemented in the meteor.
here is an example:
model.find({"field": /search/}).exec(function(err, model){
construct(model);
});
now normally everything works fine except when I use the regex.
and I know what's the problem but I cannot fix it.
the problem, as you have guessed it, is when the regex /regexParameter/ when sent by ajax to server, is converted to "/regexParameter/" and the single quotes(or double) make the regex a normal string.
in the server I have something like this:
var findObject = req.query.findObject // {"field": "/search/"} :(
req.models[config.table]
.find(findObject)
.exec(function(err, model){
return res.json({
error: err,
result: model,
});
});
is there anything I can do to make this work without writing like 100 of lines of code that iterates through each of the findObject and matches every string for a regex...?
Thanks everyone
You are right - you cannot pass RegExp objects between client and server because during serialization they are converted to strings.
Solution? (or perhaps - a workaround)
Use $regex operator in your queries, so you don't need to use RegExp objects.
So this:
{
field: /search/
}
Becomes this:
{
field: {
$regex: 'search'
}
}
Or, giving a case-insensitive search example:
{
field: {
$regex: 'search',
$options: 'i'
}
}
(instead of field: /search/i)
Read more about $regex syntax here (including about some of its restrictions).

Pre-routing with querystrings with Express in Node JS

I'm trying to use express to parse the querystring in case certain parameters are set and execute a little piece of code, before the actual routing is happening. The use-case is to grab a certain value, that could be set, independant of what link is being used. I use express' functionality to pass the stuff to the next possible rule using next().
So far, I tried - at the very top of all the app.get/post-rule-block:
app.get('[?&]something=([^&#]*)', function(req, res, next) {
var somethingID = req.params.something;
// Below line is just there to illustrate that it's working. Actual code will do something real, of course.
console.log("Something: "+somethingID);
next();
})
app.get('/', site.index);
and also:
app.param('something', function(req, res, next) {
var somethingID = req.params.something;
console.log("Something: "+somethingID);
next();
})
app.get('/', site.index);
Example of what should be triggered:
URL: www.example.com/?something=10239
URL: www.example.com/superpage/?something=10239
URL: www.example.com/minisite/?anything=10&something=10239
Unfortunately, none of my solutions actually worked, and all that happens is, that the next matching rule is triggered, but the little function above is never executed. Anybody have an idea, of how this can be done?
EDIT: I do understand, that the param-example wasn't working, as I'm not using said parameter within any other routing-rule afterwards, and it would only be triggered then.
I also do understand, that logic implies, that Express ignores the querystring and it is normally parsed within a function after the routing already happened. But as mentioned, I need this to be "route-agnostic" and work with any of the URL's that are processed within this application.
express does not allow you to route based on query strings. You could add some middleware which performs some operation if the relevant parameter is present;
app.use(function (req, res, next) {
if (req.query.something) {
// Do something; call next() when done.
} else {
next();
}
});
app.get('/someroute', function (req, res, next) {
// Assume your query params have been processed
});
Ok, there is quite a logical flaw in here. Routing only uses the URL, and ignores the querystring.
The (or better "A") solution is actually this:
app.get('*', function(req, res, next) {
if (req.query.something) {
console.log("Something: "+req.query.something);
};
next();
})
Explanation: As Express is ignoring the querystring for the routing, the only regular expression matching all URL's is "*". Once that is triggered, I can check if said querystring is existing, do my logic and continue the routing matching the next rule by using "next()".
And yes: facepalm

Regular Expression in Node.js Express Router

I have tried to find a way to enter regular expression into an express routing URL and then access the variable portion of the URL through the request object. Specifically I want to route to the url "/posts/" + any number of digits. Is there a way to do this?
Examples:
/posts/54
/posts/2
/posts/546
This should do it:
app.get('/posts/:id(\\d+)', function(req, res) {
// id portion of the request is available as req.params.id
});
EDIT: added regex to path to limit it to digits
I agree with Johnny, my only addition being that you can do this for any number of levels. For example:
app.get('/users/:id/:karma', function(req, res){
//Both req.params.id and req.params.karma are available parameters.
});
You should also check out the express documentation: http://expressjs.com/api.html.
The request section would probably prove quite useful to you.