node

Koa代理Http请求

2017-09-04  本文已影响3234人  ChrysAwesome

Koa 代理http请求,解决跨域问题

1、为什么用Koa做跨域代理?

"最初为了解决跨域问题,我把站点部署到了nginx上就解决了问题。一次偶然的面试机会,面试官提出了一个假设我需要对提交api和api返回的数据进行适配,那么nginx是不是就无法满足了。当然这个问题的提出,让我考虑到其实如果自己搭一个站点,通过这个站点进行转发,适配第三方api的请求和应答不就好了。那么要搭一个站点的语言其实有很多,例如.net,java,nodejs,php...,那为什么最后选择nodejs呢?对于我来说最重要的原因,应该就是nodejs的轻量级和javascript语言亲和性。

2、搭建nodejs应用

由于Koa2刚出,毕竟学技术,那么就学最新的。
既然搭建程序那么就从程序的入口开始做,首先写程序的路由

const fs = require('fs')
const Router = require('koa-router');
const {httpHandle} = require('../Infrastructure/httpHandle');
const koaBody = require('koa-body')({
    multipart :true
});

const render = (page) => {
    return new Promise((resolve, reject) => {
        let viewUrl = `./view/${page}`
        fs.readFile(viewUrl, "binary", (err, data) => {
            if (err) {
                reject(err)
            } else {
                resolve(data)
            }
        })
    })
}

let api = new Router();

api.get('*', httpHandle)
    .post('*', koaBody, httpHandle)
    .put('*', koaBody, httpHandle).del('*', koaBody, httpHandle);

let common = new Router();
common.get('*', async (ctx) => {
    ctx.body = await render('index.html');
})

let router = new Router();
router.use('/api', api.routes(), api.allowedMethods());
router.use('/', common.routes(), common.allowedMethods());
module.exports = router;

其次就是处理代理的请求

const httpRequest = (ctx) => {
    return new Promise((resolve) => {
        delete ctx.request.header.host;
        const options = {
            host,
            port,
            path: ctx.request.url.substr(4, ctx.request.url.length),
            method: ctx.request.method,
            headers: ctx.request.header
        }
        let requestBody='',
            body,
            head,
            chunks = [],
            fileFields,
            files,
            boundaryKey,
            boundary,
            endData,
            filesLength,
            totallength = 0;

        if (ctx.request.body) {
            console.log(ctx.request.header['content-type'])
            if (ctx.request.header['content-type'].indexOf('application/x-www-form-urlencoded') > -1) {
                requestBody = query.stringify(ctx.request.body);
                options.headers['Content-Length'] = Buffer.byteLength(requestBody)
            } else if (ctx.request.header['content-type'].indexOf('application/json') > -1) {
                requestBody = JSON.stringify(ctx.request.body);
                options.headers['Content-Length'] = Buffer.byteLength(requestBody)
            } else if (ctx.request.header['content-type'].indexOf('multipart/form-data') > -1) {
                fileFields = ctx.request.body.fields;
                files = ctx.request.body.files;
                boundaryKey = Math.random().toString(16);
                boundary = `\r\n----${boundaryKey}\r\n`;
                endData = `\r\n----${boundaryKey}--`;
                filesLength = 0;

                Object.keys(fileFields).forEach((key) => {
                    requestBody +=  `${boundary}Content-Disposition:form-data;name="${key}"\r\n\r\n${fileFields[key]}`;
                })

                Object.keys(files).forEach((key) => {
                    requestBody += `${boundary}Content-Type: application/octet-stream\r\nContent-Disposition: form-data; name="${key}";filename="${files[key].name}"\r\nContent-Transfer-Encoding: binary\r\n\r\n`;
                    filesLength += Buffer.byteLength(requestBody,'utf-8') + files[key].size;
                })

                options.headers['Content-Type'] = `multipart/form-data; boundary=--${boundaryKey}`;
                options.headers[`Content-Length`] = filesLength + Buffer.byteLength(endData);
            } else {
                requestBody = JSON.stringify(ctx.request.body)
                options.headers['Content-Length'] = Buffer.byteLength(requestBody)
            }
        }

        const req = http.request(options, (res) => {
            res.on('data', (chunk) => {
                chunks.push(chunk);
                totallength += chunk.length;
            })

            res.on('end', () => {
                body = Buffer.concat(chunks, totallength);
                head = res.headers;
                resolve({head, body});
            })
        })

        ctx.request.body && req.write(requestBody);

        if (fileFields) {
            let filesArr = Object.keys(files);
            let uploadConnt = 0;
            filesArr.forEach((key) => {
                let fileStream = fs.createReadStream(files[key].path);
                fileStream.on('end', () => {
                    fs.unlink(files[key].path);
                    uploadConnt++;
                    if (uploadConnt == filesArr.length) {
                        req.end(endData)
                    }
                })
                fileStream.pipe(req, {end: false})
            })
        } else {
            req.end();
        }

    })
}

由此简单的几行代码就实现了通过nodejs实现跨域的请求代理。 github链接

nginx代理config配置 如下

server {
      listen          1024; 
      server_name     tigrex:1024;
      root            home/TuoTuo.v2.UI;
      index           index.html;
      access_log      logs/tigrex.access.log;
      error_log       logs/tigrex.error.log;

      charset         utf-8;
      
      location /api {
         proxy_pass    http://127.0.0.1:1023/;
         proxy_set_header Host $host;
         proxy_redirect off;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      }   
      
      location / {
          try_files $uri $uri/ /index.html;          
        }
    }

上一篇下一篇

猜你喜欢

热点阅读