• 跨域解决方案


    1. 定义

    当用户需要请求数据时, 用户向前端服务器发送请求, 然后前端服务器接收请求之后向后端服务器发送请求接收数据, 然后转发给用户.

    node 转发的本质其实和webpack devServer 的本质是一样的, 只不过node 转发一般由自己实现, webpack devServer 是一个定义好的配置.

    node 转发跨域理解成为webpack devServer 原理的实现

    2. 代理转发

    参见:跨域解决方案 - webpack devServer.md

    3. node 转发解决跨域问题

    express(app.js)

    const express = require('express')
    
    const log = console.log.bind(console)
    const app = express()
    
    // cors 模块用来解决跨域问题,只要声明了 cor,就说明该服务器允许跨域的访问
    // const cors = require('cors')
    
    // app.use(cors())
    
    app.get('/helloworld', (request, response) => {
        log('触发了该事件')
        response.send('hello')
    })
    
    app.get('/singlecors', (request, response) => {
        response.set('Access-Control-Allow-Origin', '*')
        response.send('hello')
    })
    
    app.get('/api/todos', (request, response) => {
        response.send('request todos')
    })
    
    const main = () => {
        let server = app.listen(2300, () => {
            let host = server.address().address
            let port = server.address().port
    
            log(`应用实例,访问地址为 http://${host}:${port}`)
        })
    }
    
    if (require.main === module) {
        main()
    }
    

    node 转发代码

    const http = require('http')
    const https = require('https')
    const fs = require('fs')
    const url = require('url')
    
    const express = require('express')
    const bodyParser = require('body-parser')
    
    const SERVER = require('./server.config').server
    const log = console.log.bind(console)
    
    const app = express()
    app.use(express.static('proxy'))
    app.use(bodyParser.json())
    
    const clientByProtocol = (protocol) => {
        if (protocol === 'http:') {
            return http
        } else {
            return https
        }
    }
    
    const httpOptions = (request) => {
        let server = SERVER
        // 把 server 网址解析成一个 url 对象, 方便发请求的时候使用
        let o = url.parse(server)
        log('o: ', o)
        // 把浏览器发送的请求的 headers 全部添加到 options 中,
        // 避免出现漏掉某些关键 headers(如 transfer-encoding, connection 等) 导致出 bug 的情况
        let headers = Object.assign({}, request.headers)
        // 组合成最终发送的请求格式
        let options = Object.assign({}, {
            headers: headers,
        }, o)
        options.method = request.method
        // request.originalUrl 不仅包含 path, 还包含 query string
        options.path = request.originalUrl
        return options
    }
    
    // 当访问主页时, 返回对应的HTML 内容
    app.get('/', (request, response) => {
        log('here')
        fs.readFile('index.html', 'utf8', (error, data) => {
            response.set('Content-Type', 'text/html; charset=UTF-8')
            response.send(data)
        })
    })
    
    // 将服务器的响应转发至浏览器
    const sendResponseToClient = (httpResponse, expressResponse) => {
        // 有两个响应对象, 一个是 http 响应对象, 另一个是 express 响应对象
        let r = httpResponse
        let response = expressResponse
    
        // 设置响应对象的状态码和头部字段
        response.status(r.statusCode)
        Object.entries(r.headers).forEach(([k, v]) => {
            response.setHeader(k, v)
        })
        // 当接收到数据的时候触发 data 事件, 然后把数据发送给客户端
        r.on('data', (chunk) => {
            response.send(chunk)
        })
        // 数据发送完成时触发 end 事件, express 对象告诉客户端数据发送完毕
        r.on('end', () => {
            response.end()
        })
        // 往客户端发送数据的过程中出错
        r.on('error', () => {
            log('error to request')
        })
    }
    
    // 将浏览器发送过来的请求转发至服务器
    const sendRequestToServer = (request, response) => {
        // 根据当前request 以及后端接口信息, 定义新的请求格式
        let options = httpOptions(request)
        // log('options: ', options)
        // 根据协议来选择用 http 模块还是 https 模块发送
        let client = clientByProtocol(options.protocol)
    
        // 使用http/https 定义请求
        let req = client.request(options, (res) => {
            // 收到 server 传过来的响应后, 把这个响应发送给客户端(也就是浏览器)
            sendResponseToClient(res, response)
        })
        // 监听 error 事件, 也就是往 server 发送请求的过程中发生错误会触发这个事件
        req.on('error', (e) => {
            log(`往 server(${request.url}) 发送请求报错`, e)
        })
        // 如果发送的请求方法不是 GET, 说明 request.body 有数据
        // 此时也要把数据发给 server
        if (options.method !== 'GET') {
            let body = request.body
            let chunk = JSON.stringify(body)
            req.write(chunk)
        }
        // 完成发送请求
        req.end()
    }
    
    // 拿到浏览器发送的以 /api/ 开头的请求, 这些请求表述数据请求, 需要转发至后端服务器
    app.all('/api/*',(request, response) => {
        sendRequestToServer(request, response)
    })
    
    const run = (port, host) => {
        let server = app.listen(port, host, () => {
            let address = server.address()
            log(`listening ${address.address}, ${address.port}`)
        })
    }
    
    if (require.main === module) {
        let port = 3300
        let host = 'localhost'
        run(port, host)
    }
    

    4. demo 地址

    node 转发跨域解决方案

    PS: 当项目运行成功时, 需要在浏览器中输入url: 127.0.0.1:3300, 获取index.html 文件

  • 相关阅读:
    VS学习笔记2
    VS学习笔记
    分享几个有趣的小程序
    关于类型的转换(抄来的 ,留着,感觉有用。)
    现在觉得IT还挺有意思
    DataGrid 查出一个列 按要求显示格式 例如:操作人(地点)
    WPF DataGrid 列显示0,-1(作废、删除)状态,1,2(支出、收入)类型,操作人(在其他表中),如何转换格式。
    WPF DataGrid中鼠标双击某一列,弹出窗体作为(增加、修改、详细)按钮的快捷键。
    “指定的参数已超出有效值的范围”在【 parameterUpdate.Add(new OracleParameter("STATUS", 0));】报错
    WPF StoreDataSetPaginator
  • 原文地址:https://www.cnblogs.com/oulae/p/12784189.html
Copyright © 2020-2023  润新知