Wildcard routes (Angular and Express)

1,360 views
Skip to first unread message

Kyle Roche

unread,
Jan 18, 2013, 5:56:43 PM1/18/13
to ang...@googlegroups.com
In an Express / Angular app serving static HTML (not Jade) from /public we have the following in our app.js file so that html5Mode works for partials. Something doesn't seem right here... 

// This route deals enables HTML5Mode by forwarding missing files to the index.html
app.get('/*', function(req, res) {
  // Just send the index.html for other files to support HTML5Mode
  res.sendfile('index.html', { root: path.resolve(__dirname + '/public') });
});

This causes a few things to happen... all routes return 200 even if invalid, which is making test driven process impossible. IN addition, everything is getting served as if it was index.html, which we don't want... is there a way to change app.js to: 
1) server defined routes (check... not a problem here. node routes work fine)... 
2) then serve partials (public/partials/*.html) if not found
3) and 404 anything else? 

Thanks!
Kyle

Will Vincent

unread,
Jan 18, 2013, 11:47:33 PM1/18/13
to ang...@googlegroups.com
Probably what you'd need to do is something along these lines:

First, respond to requests for defined server routes.  Then, check against a list of allowed html5 route patterns and redirect those requests through index.html
and finally, serve up static content or 404.

Since you need the angular routes to actually be handled by index.html for html5 routing to work I don't really see any way you could get around having some sort of look up table/list/array/etc that defines patterns for those routes on the server side.

I'm not entirely sure how to go about setting that up, but that seems like the proper progression of logic for this.  Check node routes, then check html5 routes, then serve static content (partials, css, images, etc), and finally, if none of those succeed, respond with a 404.

Will Vincent

unread,
Jan 19, 2013, 12:07:12 AM1/19/13
to ang...@googlegroups.com
Actually, I think something like this should probably work..

app.configure(function() {
  app.use(app.router);
  app.use(express.static(__dirname + '/../public'));
  app.use(function(req, res) {
    res.status(404);

    // Respond with html page:
    if (req.accepts('html')) {
      res.render('404', { url: req.url });
      return;
    }
    // Respond with JSON:
    if (req.accepts('json')) {
      res.send({ error: 'Not found'});
      return;
    }

    // Default to plain text:
    res.type('txt').send('Not found');
});

// Some node routes
app.get('/foo', function(req, res) {
  res.send('foo');
});

// ...

// Define html5 routes used by angular app:
app.get('/user*', html5Route);
app.get('/user', html5Route);
app.get('/login', html5Route);
app.get('/logout', html5Route);
app.get('/posts*', html5Route);
app.get('/posts', html5Route);
//...

// Force all matching html5 routes through index.html:
function html5Route(req, res, next) {
  res.sendfile(__dirname + '/public/index.html');
}

Kyle Roche

unread,
Jan 19, 2013, 12:09:26 AM1/19/13
to ang...@googlegroups.com
Interesting approach... thanks! I'll try this out. I'm trying to figure out the drawback of just living w/ # in the angular routes. Is there a big disadvantage there? Other than it's confusing for the user. 


--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en-US.
 
 



--
kyleRoche (@kylemroche)
Founder / CTO

Will Vincent

unread,
Jan 19, 2013, 12:32:11 AM1/19/13
to ang...@googlegroups.com
IMHO having or omitting the hash or hashbang is a purely aesthetic choice..  Omitting it somewhat obfuscates the fact that it's a single page app, which may be desirable in some situations.  But I don't know that showing a hash or hashbang is necessarily confusing for users.  Twitter had a hashbang in their urls until fairly recently as I recall.  It doesn't affect bookmarking.. *shrug*  There's not much downside to it that I can think of, unless there is perhaps an SEO benefit to omitting them.

*shrug*

Joshua Miller

unread,
Jan 19, 2013, 3:05:08 AM1/19/13
to angular
Hello!

I'm a little confused by the original question. Kyle said "all routes return 200 even if invalid, which is making test driven process impossible". If you are creating a single-page application, the JavaScript is performing the routing, so *it* knows what is a valid route and *not* the server. Why do you want the server to *also* know what constitutes a valid route? You now have to code for 404 in two different places, which seems pretty awkward.

A good server-side setup in my opinion says "if it's a real file, serve it, otherwise it's none of our business, so just serve index.html and let the client-side deal with it".

I'm also unsure what issue you were having with TDD. We use TDD for all our apps, but still forward the wildcard and do not have problems. Can you elaborate on the issue here?

Josh


On Fri, Jan 18, 2013 at 9:32 PM, Will Vincent <tci...@gmail.com> wrote:
IMHO having or omitting the hash or hashbang is a purely aesthetic choice..  Omitting it somewhat obfuscates the fact that it's a single page app, which may be desirable in some situations.  But I don't know that showing a hash or hashbang is necessarily confusing for users.  Twitter had a hashbang in their urls until fairly recently as I recall.  It doesn't affect bookmarking.. *shrug*  There's not much downside to it that I can think of, unless there is perhaps an SEO benefit to omitting them.

*shrug*

--

Will Vincent

unread,
Jan 19, 2013, 4:09:23 AM1/19/13
to ang...@googlegroups.com
I tend to agree, but I could see what Kyle's trying to achieve being useful for thorough testing of both the client and the server.  If the server always returns a 200 status it would be more difficult to test it.

On the other hand, you're right, a single page app should probably do all of it's own handling of non-existent pages/routes.

Kyle Roche

unread,
Jan 19, 2013, 9:35:47 AM1/19/13
to ang...@googlegroups.com
Hi Josh 

We are moving from a Play2.0(Scala) / Backbone UI to Express/Angular SPA which is a bit of an adjustment in thinking. I agree, there are probably a few shifts in thinking. So, your explanation makes sense (as does Will's). Here's our simplified first thought: 
- Write test to fail for /api/whatever (Express route) by looking for 404
- Write code for /api/whatever
- Test pass

Can you share an example test of what you're starting w/ when you begin creating a new REST callback? 

Thanks!

Joshua Miller

unread,
Jan 19, 2013, 2:18:13 PM1/19/13
to angular
Hello!

I *love* Play Scala, so we'll get along just fine. :-)

I often have (as you seem to) static files served from the root (e.g. /index.html, /assets/favicon.ico, etc.) and the API calls placed in a subpath (i.e. /api). I do not test the presence of static files typically, but I can see how this could be done. I often include a test for "/non/existent/file" to ensure it also serves "index.html". 

As for TDD of new API calls, I usually start with something like this:

describe("/users", function () {
  it("should return a list of users", function () {
    runs( function () {
      needle.get( '/users', function ( err, res, body ) {
        expect( err ).toBeUndefined();
        expect( res.statusCode ).toBe( 200 );
        // ...
      });
    });
  });
});

This test will fail. Then I code for it, then write a test for an enhancement, then add the enhancement, etc., ad infinitum.

Obviously, some of this is redundant and I would extrapolate it away. Also, you can test expressjs without running it as a server, but I do not prefer to do so; it seems less "real" and won't catch expressjs bugs or peculiarities in how expressjs works internally. Also, I am also using needle (https://github.com/tomas/needle), as I find it flows very well and is easy to follow, along with Jasmine, Testacular, and Grunt, since I am using them already for the angular side. 

This is more end-to-end testing and is of course in addition to whatever unit testing is performed on server-side business logic.

Josh
Reply all
Reply to author
Forward
0 new messages