Node Server

145 阅读2分钟

本文总结一些自定义Server服务器时的相关业务,也包括一些值得关注的编程技巧

最简单的服务器,大概代码如下:

class Server {
    constructor(options) {
        this.port = options.port;
        this.directory = options.directory
        this.address = options.address;

        // 启动服务 
        this.start()
    }
    handleRequest() {
        console.log('handleRequest', this)
    }
    start() {
        const server = http.createServer(this.handleRequest);
        server.listen(this.port)
    }
}

处理handleRequest方法内部的this指向的问题

但需要注意,handleRequest方法内部的this将不再指向new出来的Server实例,因为this.handleRequest这个回调会传给createServer,继续执行就进入了http模块的内部,它会有它自己的一个上下文环境,因此this将会指向这个上下文环境,该问题可以通过如下方式解决:

    handleRequest() {
        return () => {
            console.log(this)
        }
    }
    start() {
        const server = http.createServer(this.handleRequest());
        server.listen(this.port)
    }

或者,如果运行的node版本支持在类中定义箭头函数的话,可以直接写成:

    handleRequest = (req, res) => {
        console.log(this)
    }

当然,在constructor里面用bind处理一下也可以

class Server {
    constructor(options) {
        this.port = options.port;
        this.directory = options.directory
        this.address = options.address;
        this.handleRequest = this.handleRequest.bind(this)
        // 启动服务 
        this.start()
    }

跨域支持

    cors(req, res) {
        // 这里运行跨域  cors 跨域资源部共享,服务端设置
        if (req.headers.origin) {
            // 任何人都可以访问此路径
            res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
            res.setHeader('Access-Control-Allow-Headers', 'authorization'); // 服务端需要允许自定义header
            res.setHeader('Access-Control-Max-Age', 10); // 10s 内发一次预检请求
            res.setHeader('Access-Control-Allow-Methods', 'GET,POST,DELETE,PUT,OPTIONS')
            // 普通请求 会变成复杂请求 
            if (req.method === 'OPTIONS') { // 允许预检请求后 真实的请求可以正常发出
                res.end()
                return true
            }
        }
    }
    handleRequest = async (req, res) => {
        // 此逻辑 处理的是静态逻辑  我把vue项目启动起来了 , 想模拟接口数据
        let { pathname, query } = url.parse(req.url, true); // /xxx

        if (this.cors(req, res)) {
            return
        }

开启压缩

需要先取到客户端传过来的accept-encoding这个头,看浏览器客户端支持哪些压缩格式,同时需要注意,response也要设置Content-Encoding以告诉浏览器返回的是什么格式,否则浏览器不认识这个格式,在chrome中就会下载这个文件

    compress(req, res) {
        let encoding = req.headers['accept-encoding']; // 浏览器会主动给我一个压缩的方式
        if (encoding) {
            if (encoding.includes('br')) {
                res.setHeader('Content-Encoding', 'br')
                return zlib.createBrotliCompress()
            } else if (encoding.includes('gzip')) {
                res.setHeader('Content-Encoding', 'gzip')
                return zlib.createGzip()
            } else if (encoding.includes('deflate')) {
                res.setHeader('Content-Encoding', 'deflate')
                return zlib.createDeflate()
            }
        }
    }

利用Referrer头来做防盗链

    sendFile(filename, statObj, req, res) {
        // 可独流 -》 可写流中 ws.write() ws.end()
        res.setHeader('Content-Type', (mime.getType(filename) || 'text/plain') + ';charset=utf-8');
        // 在发送图片的时候 对图片进行防盗链处理 
        if (/\.(jpeg)/.test(filename)) { // 如果是jpeg 则进行防盗链处理
            let referer = req.headers['referer'] || req.headers['referrer'];

            // 有人来引用这个图片了 
            if (referer) {
                let host = 'http://' + req.headers['host'];
                let r1 = url.parse(host).hostname;
                let r2 = url.parse(referer).hostname
                if (r1 !== r2 && !['http://a.zf.cn:8080/'].includes(referer)) { // 引用的人 和自己的host不一致 
                    createReadStream(path.resolve(__dirname, 'error.jpeg')).pipe(res);
                    return
                }
            }
        }
        if (this.cache(statObj, req, res)) {
            return
        }
        debugDev(filename);
        let stream = this.compress(req, res)
        if (stream) { // 如果支持压缩 则返回一个转化流
            return createReadStream(filename).pipe(stream).pipe(res);
        }
        createReadStream(filename).pipe(res)
    }

完整代码:/jiagouke07-3-node/12.http-server/src/main.js