Node.js minimal function for parsing route

2019-08-06 04:24发布

问题:

I have a Node.js / Express app working, that receives routes like so:

app.get('/resource/:res', someFunction);
app.get('/foo/bar/:id', someOtherFunction);

This is great and works fine.

I am also using Socket.IO, and want to have some server calls use websockets instead of traditional RESTful calls. However, I want to make it very clean and almost use the same syntax, preferrably:

app.sio.get('/resource/:res', someFunction);

This will give a synthetic 'REST' interface to Socket.IO, where, from the programmer's perspective, he isn't doing anything different. Just flagging websockets: true from the client.

I can deal with all the details, such as a custom way to pass in the request verbs and parse them and so and so, I don't have a problem with this. The only thing I am looking for is some function that can parse routes like express does, and route them properly. For example,

// I don't know how to read the ':bar',
'foo/:bar'

// Or handle all complex routings, such as
'foo/:bar/and/:so/on'

I could dig in real deep and try to code this myself, or try to read through all of express' source code and find where they do it, but I am sure it exists by itself. Just don't know where to find it.

UPDATE

robertklep provided a great answer which totally solved this for me. I adapted it into a full solution, which I posted in an answer below.

回答1:

You can use the Express router class to do the heavy lifting:

var io        = require('socket.io').listen(...);
var express   = require('express');
var sioRouter = new express.Router();

sioRouter.get('/foo/:bar', function(socket, params) {
  socket.emit('response', 'hello from /foo/' + params.bar);
});

io.sockets.on('connection', function(socket) {

  socket.on('GET', function(url) {
    // see if sioRouter has a route for this url:
    var route = sioRouter.match('GET', url);
    // if so, call its (first) callback (the route handler):
    if (route && route.callbacks.length) {
      route.callbacks[0](socket, route.params);
    }
  });

});

// client-side
var socket = io.connect();
socket.emit('GET', '/foo/helloworld');

You can obviously pass in extra data with the request and pass that to your route handlers as well (as an extra parameter for example).



回答2:

robertklep provided a great answer which totally solved this for me. I adapted it into a full solution, which is below in case others want to do something similar:

Node (server side):

// Extend Express' Router to a simple name
app.sio = new express.Router();
app.sio.socketio = require('socket.io').listen(server, { log: false });

// Map all sockets requests to HTTP verbs, which parse
// the request and pass it into a simple callback.
app.sio.socketio.sockets.on('connection', function (socket) {
  var verbs = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
  for (var i = 0; i < verbs.length; ++i) {
    var go = function(verb) {
      socket.on(verb, function (url, data) {
        var route = app.sio.match(verb, url);
        if (route && route.callbacks.length) {
          var req = {url: url, params: route.params, data: data, socket:socket}
          route.callbacks[0](req);
        }  
      });
    }(verbs[i]);
  }
});

// Simplify Socket.IO's 'emit' function and liken
// it traditional Express routing.
app.sio.end = function(req, res) {
  req.socket.emit('response', req.url, res);
}

// Here's an example of a simplified request now, which 
// looks nearly just like a traditional Express request.
app.sio.get('/foo/:bar', function(req) {
  app.sio.end(req, 'You said schnazzy was ' + req.data.schnazzy);
});

Client side:

// Instantiate Socket.IO
var socket = io.connect('http://xxxxxx');
socket.callbacks = {};

// Similar to the server side, map functions
// for each 'HTTP verb' request and handle the details.    
var verbs = ['get', 'post', 'put', 'path', 'delete'];
for (var i = 0; i < verbs.length; ++i) {
  var go = function(verb) {
    socket[verb] = function(url, data, cb) {
      socket.emit(String(verb).toUpperCase(), url, data);
      if (cb !== undefined) {
        socket.callbacks[url] = cb; 
      }
    }
  }(verbs[i]);
}

// All server responses funnel to this function,
// which properly routes the data to the correct
// callback function declared in the original request.
socket.on('response', function (url, data) {
  if (socket.callbacks[url] != undefined) {
    socket.callbacks[url](data);
  }
});

// Implementation example, params are:
// 1. 'REST' URL,
// 2. Data passed along,
// 3. Callback function that will trigger
//    every time this particular request URL
//    gets a response.
socket.get('/foo/bar', { schnazzy: true }, function(data){
  console.log(data); // -> 'You said schnazzy was true'
});

Thanks for your help, robertklep!