senecajs / seneca-web

Http route mapping for Seneca microservices.
MIT License
76 stars 44 forks source link

How to set the content-type of result, if it is not application/json #119

Open martin-mueller-solutions opened 7 years ago

martin-mueller-solutions commented 7 years ago

Hi everybody,

I am trying to access a microservice via a restful api-gateway which is working nicely. But if the microservice returns something else (like an image) I have the problem that seneca refuses to return it to the client/browser.

I use the following code to set up my gateway:

let Routes = [{
    pin: 'role:gateway,cmd:media,action:*',
    prefix: '/api',
    map: {
        get: {GET: true, suffix: '/:mediumId', name: 'media'}
    }
}];

const httpServer = express();

let senecaConfig = {
    routes: Routes,
    adapter: require('seneca-web-adapter-express'),
    context: httpServer
};

let seneca = Seneca({
    timeout: 15000,
    log: {level: 'info+'},
    errhandler: (err) => {
        console.error(err);
    }
}).use('seneca-amqp-transport')
    .use(plugins)
    .use(Web, senecaConfig)
    .ready(() => {
        let server = seneca.export('web/context')();

        server.listen('4000', () => {
            console.log('server started on: 4000')
        })
    }).client({
        type: 'amqp',
        pin: 'role:gateway,cmd:*',
        url: 'amqp://' + config.addr + ':' + config.port
    });

This setup works great for all json-like data, since it connects to an amqp-queue.

I know that the payload for the queue has to be text-based, that is why I base64-encode my binary data, which works fine. I just need to alter the output to the browser to not send json-data but change the headers and just send the binary-payload.

What would be the right way for doing that. Intercepting seneca before it returns data to the client, or just write a middleware-function for express? Where would I register that? Right now, the middleware-function gets called before the response is built.

Thanks in advance

tswaters commented 7 years ago

It's possible if both the seneca route handler and seneca-web are on the same microservice. You should have access to request$ and reply$ and which are req and res. Just set autoreply to false for the route, append content-type header and respond manually via reply$ (which is res)

Of course, if you are using microservices where the route handler is in a different service, reply$ and response$ are stripped out of the payload and you won't be able to. This is a pretty common way to use the module and currently there is no way to overwrite content type in this way.

What I would do is fork seneca-web-adapter-express, create your own version and update the following section: https://github.com/senecajs/seneca-web-adapter-express/blob/master/seneca-web-adapter-express.js#L52-L84

The above section basically the route handler, i.e. (req, res) => {} - with additional stuff like the seneca context, routes, etc. You can potentially read stuff from the seneca response, i.e. if you always specify a key in the return object called content-type you can check if it's there and set the header appropriately using res.

I'm not entirely sure if this is something that should go into the adapters - at least not in the immediate future, without a breaking change and some standards about what the keys int he response should be. We would also need to update all the adapters to function in a similar fashion, and I'm not sure if it's going to happen.

At my work, we've forked seneca-web-adapter-express and have all sorts of additional stuff in there, including content type - we also have a special key for value to return a string of text. We also customize the payload section above to pass along additional context to the seneca action like session, cookies and certain app.locals

ggn06awu commented 7 years ago

Thanks for this @tswaters, this issue could really do with some attention in the mainstream Seneca repo's. In the few tutorials / examples it's actually recommended to have the router and web server served by separate microservices.