How to implement HMVC structure in Node/Express

2019-06-14 06:04发布

问题:

I have several "api" endpoints in my application that are used by the front-end framework for its AJAX processes. For organizational purposes, I would like to re-use the code for these endpoints to retrieve data for server-side rendering, which I do in some instances to play nicely with search engines. So, ideally I would implement some sort of HMVC setup to simply access a route on the API endpoint. Specifically I would like to get the response and perform additional actions on it before issuing the top-level response (e.g., render a view with results).

For example:

app.get('/post/recent', function(req, res) {
    app.doRequest('/api/posts/', req, function(res2) {
        var data = res2.body;
        res.render('posts/index', data);
    });
});

What's the best way to do this?

So far the best option I've come up with is:

Expose all logic in an endpoint as a method, which would be used in app.get('...', myFunction), and then I could call myFunction elsewhere outside of the express flow for that path. However, this would not give me a reliable way to run middleware specific to the endpoint (which I would also want to run on the HMVC request) unless I wrote my own middleware implementation that did not rely on express. The API endpoint has middleware that does something like if(!hasAccess) res.send(403), which I specifically do NOT want to happen in my main route since I'd want to render a nice error page instead of just sending an error code.

Example:

var getPosts = function(req) {
   var deferred = q.defer()
   doDatabaseQuery(req.query).then(function(response) {
       deferred.resolve(response)
   });
};
app.get('/api/posts', myMiddlewareFunction(), function(req, res) {
   getPosts(req).then(function(response) {
        res.send(response);
   });
);
app.get('/post/recent', function(req, res) {
   // I want to run middleware here, not in root level
   getPosts(req).then(function(response) {
        res.render('post/index', response);
   }, function(err) {
        res.render('error/noaccess');
   });
});

Any better ideas? Is there a way to programmatically access an express route and get the result?

回答1:

I figured this out by diving into the Express source code. There is a method called handle which I can manually invoke with modified request and response objects to get the effect I want:

app.get '/posts', (req, res) ->
req.url = '/api/posts'
newRes = _.clone(res)
newRes.send = (data, code)->
    if code is 200
        return res.render('posts/index', data)
    else
        return res.render('error')
app.handle(req, newRes)