首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HapiJS代理故障

HapiJS代理故障
EN

Stack Overflow用户
提问于 2015-03-09 18:47:57
回答 1查看 3.2K关注 0票数 1

TL;DR

如何拦截请求,为ID选择不同的路由,将ID存储在会话中,然后使用刚获得的ID继续原始请求(特别是PUT/POST )?

背景

我使用HapiJS (8)将客户端的请求代理到现有的API (在那里,我无法控制进程/逻辑)。API要求每个请求在查询字符串或有效负载中包含一个“会话ID”(取决于http方法)。为了得到会话ID,我要做的就是要一个.不需要用户名/pwd(它在头中使用基本auth )。如果不更新会话ID,则每24小时过期一次。每个客户端都有自己的会话ID。

我目前正在使用哈皮饼干来存储会话ID的值,在需要ID时会查询该值。如果ID过期或null,我需要在客户端的请求成功地代理到API之前请求一个新的ID。

电流溶液

当客户端的请求方法是“GET”时,我很好地利用了在appendNext中描述的appendNext配置来处理这个挑战。请求被hapi-auth-cookie拦截,如果需要一个新的会话ID,则将请求发送到该特定路由以获得请求,API返回分配给Hapi会话的ID,然后(使用沉船)一个reply.redirect返回原始的get请求,在它完成该请求时。天衣无缝,优雅。

但是,我不知道如何使用包含数据有效负载的不同http方法来实现相同的流。

除了reply.redirect之外,还有什么东西可以在维护原始有效载荷和方法的同时实现相同的目标吗?还是总体上有更好的方法来做到这一点?

代码(用于当前用于“GET”请求的内容)

主应用程序文件(hapi-auth-cookie信托)

代码语言:javascript
复制
# register plugins
server.register require('./plugins')
, (err) ->
    throw err if err

    # set authentication (NLS) configs
    server.auth.strategy 'session', 'cookie',
        password: 'session_pwd'
        cookie: 'ghsid'
        redirectTo: '/api/SessionID' #get new session ID
        isSecure: config.get 'ssl'
        appendNext: true
        ttl: config.get 'session_length'

利用会话身份验证并调用hapi插件的控制器:

代码语言:javascript
复制
simpleRequest:
    auth: 'session'
    handler: (request, reply) ->
        qs = Qs.stringify request.query
        request.papi_url = "/api/route/sample?#{qs}"

        reply.proxy
            mapUri: (request, reply) ->
                auth = config.get 'basic_auth'
                api_host = config.get 'api_host'
                papi_url = request.papi_url
                path = api_host + papi_url
                next null, path, {authorization: auth}

获取新会话ID的路由

代码语言:javascript
复制
module.exports = [

    {
        path: '/api/SessionID'
        method: 'GET'
        config: SessionController.session
    }

]

会话控制器

代码语言:javascript
复制
Wreck       = require 'wreck'
config      = require 'config'

module.exports =
    session:
        description: 'Get new session ID'

        auth:
            mode: 'try'
            strategy: 'session'

        plugins:
            'hapi-auth-cookie':
                redirectTo: false

        handler: (request, reply) ->

            # request configs
            papi_url = "/Session"
            api_host = config.get 'api_host'
            url = api_host + papi_url
            opts =
                headers:
                    'Authorization': config.get 'basic_auth'
                    'content-type': 'application/json;charset=UTF-8'

            # make request to PAPI
            Wreck.post url, opts, (err, res, body) ->
                throw new Error err if err

                try
                    bdy = JSON.parse body
                    sess =
                        nls: bdy.SessionId

                    if bdy.SessionId
                        # authenticate user with NLS
                        request.auth.session.set sess

                        # redirect to initial route
                        reply.redirect request.url.query.next

                    else
                        return throw new Error

                catch err
                    throw new Error err

最终解

基于Matt的回答,我创建了一个自定义插件,它被注册为身份验证方案,这样我就可以控制每个路由。

下面是插件代码:

代码语言:javascript
复制
Wreck           = require 'wreck'
config          = require 'config'

exports.register = (server, options, next) ->
    server.auth.scheme 'cookie', internals.implementation
    next()

exports.register.attributes =
    name: 'Hapi Session Interceptor'
    version: '1.0.0'

internals = {}

internals.implementation = (server, options, next) ->

    scheme = authenticate: (request, reply) ->

        validate = ->

            session = request.state.sessionID
            unless session
                return unauthenticated()

            reply.continue(credentials: {'session': session})

        unauthenticated = ->

            api_url = "/SessionID"
            api_host = config.get 'api_host'
            url = api_host + api_url
            opts =
                headers:
                    'Authorization': config.get 'basic_auth'
                    'content-type': 'application/json;charset=UTF-8'

            # make request to API
            Wreck.post url, opts, (err, res, body) ->
                throw new Error err if err

                bdy = JSON.parse body
                sess =
                    session: bdy.SessionId

                if bdy.SessionId
                    reply.state 'sessionID', bdy.SessionId
                    reply.continue(credentials: sess)

                else
                    return throw new Error

        validate()

    return scheme
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-10 21:05:47

虽然我并不完全忠实于您的代码,但我已经列出了一个示例,其中包含了我认为您正在使用的所有部分。

我制作了一个service插件来表示您的API。upstream插件表示您要代理的实际上游API。

所有请求都通过service传递并代理到upstreamupstream只输出它接收到的所有头和有效负载。

如果原始请求不包含带有sessionId的cookie,则会在upstream上命中一条路径以获得该路径。然后,当响应返回流时,使用此值设置cookie。

代码在这里:https://github.com/mtharrison/hapijs-proxy-trouble

用卷发和你的浏览器试试。

GET: curl http://localhost:4000

POST W/有效载荷: curl -X POST -H "content-type: application/json" -d '{"example":"payload"}' http://localhost:4000

index.js

代码语言:javascript
复制
var Hapi = require('hapi');

var server = new Hapi.Server();

server.connection({ port: 4000, labels: ['service'] }); // Your service
server.connection({ port: 5000, labels: ['upstream']}); // Pretend upstream API

server.state('session', {
    ttl: 24 * 60 * 60 * 1000,
    isSecure: false,
    path: '/',
    encoding: 'base64json'
});

server.register([{
    register: require('./service')
}, {
    register: require('./upstream')
}], 
function (err) {

    if (err) {
        throw err;
    }

    server.start(function () {

        console.log('Started!');
    });

});

service.js

代码语言:javascript
复制
var Wreck = require('wreck');

exports.register = function (server, options, next) {

    // This is where the magic happens!

    server.select('service').ext('onPreHandler', function (request, reply) {

        var sessionId = request.state.session;

        var _done = function () {

            // Set the cookie and proceed to the route

            request.headers['X-Session-Id'] = sessionId;
            reply.state('session', sessionId);
            reply.continue();
        }

        if (typeof sessionId !== 'undefined')
            return _done();

        // We don't have a sessionId, let's get one

        Wreck.get('http://localhost:5000/sessionId', {json: true}, function (err, res, payload) {

            if(err) {
                throw err;
            }

            sessionId = payload.id;

            _done();
        });
    });

    server.select('service').route({
        method: '*',
        path: '/{p*}',  // Proxies all routes and methods
        handler: {
            proxy: {
                host: 'localhost',
                port: 5000,
                protocol: 'http',
                passThrough: true
            }
        }
    });

    next();
};

exports.register.attributes = {
    name: 'your-service'    
};

upstream.js

代码语言:javascript
复制
exports.register = function (server, options, next) {

    server.select('upstream').route([{
        method: '*',
        path: '/{p*}',
        handler: function (request, reply) {

            // Just prints out what it received for headers and payload
            // To prove we got send the original payload and the sessionID header

            reply({
                originalHeaders: request.headers,
                originalPayload: request.payload,
            })
        }
    }, {
        method: 'GET',
        path: '/sessionId',
        handler: function (request, reply) {

            // Returns a random session id

            reply({ id: (Math.floor(Math.random() * 1000)) });
        }
    }]);

    next();    
};

exports.register.attributes = {
    name: 'upstream'    
};
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28949559

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档