阿美利加大统领得小感冒引发的思考: ajax+node+promise+nginx 实现一次简单的本地联调

最近在练习前后端联调的时候,做了一个练习:

  1. 通过前端页面localhost:8001的ajax 向后端服务器localhost:8000发送一个路由为api/getNews的请求,服务器返回 阿美利加大统领得感冒的新闻,并在返回对象中带了一个commentsURL路由对象。
  2. 前端页面解析返回的response,解析出commentURL路由,并根据这个路由拿到关于 阿美利加大统领得感冒 这个新闻的评论。
  3. 为了突出联调过程本身,对服务器逻辑做了最大程度的简化。server服务器没有链接数据库,因此也不需要请求URL中包含诸如?id=123&time=124124这样的query部分,而是简单地根据路由返回的假数据。
  4. 整个过程全部用原生的javascript 完成,没有用到 jQuery 和 express 这样的库。

server部分

下面是server部分的代码:

const http = require('http')
const querystring = require('querystring')
const PORT = 8000;

const server = http.createServer((req, res) => {
    const method = req.method
    const url = req.url
    const path = url.split('?')[0]
    const host = req.headers.host
    const query = querystring.parse(url.split('?')[1])
    res.setHeader('Content-type', 'application/json')

    const resData = {}
    console.log(resData)
    if (method === 'GET') {
        // 处理不同路由
        if (path === '/api/getNews') {
            resData.title = "阿美利加大统领确诊新小感冒"
            resData.content = "blablablablablabla"
            resData.commentURL = `/getComments?id=${query.id}`
        }
        if (path === '/api/getComments') {
            resData.comments = "卧槽这老头太牛逼了!"
        }
        res.end(
            JSON.stringify(resData)
        )
    }

})
server.listen(PORT)

就是建立一个服务器监听8000端口,如果GET请求的路由是/api/getNews, 那么返回阿美利加大统领得感冒的新闻; 如果GET请求的路由是 /api/getComments ,那么返回“卧槽这老头太牛逼了!”的评论。

nginx 反向代理

由于我们的页面和服务器都是在localhost上,因此我们要给返回页面的http-server和返回新闻数据的server设置不同的端口以免冲突。这里给http-server设置8001端口,而返回新闻数据的server还是之前设置的8000端口。
但是由于CORS 同源策略,这样直接跨域访问Chrome是会报错的,因此我们要用到nginx反向代理,将http-server和返回新闻数据的server的请求统一到一个端口:比如说8080
具体的逻辑是:
如果请求的路由是 localhost:8080/(由浏览器发出),那么nginx会将请求代理到 localhost:8001上,也就是http-server所在的端口,这时候8001端口会将html和依赖的img,css,js等文件返回;如果请求的路由是localhost:8080/api(由html页面的ajax请求发出),那么nginx会将请求代理到 localhost:8000上,也就是返回新闻数据的server上。

nginx的配置很简单:

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    
    sendfile        on;

    keepalive_timeout  65;

    server {
        listen       8080;
        server_name  localhost;
        		
		location / {
			proxy_pass http://localhost:8001;
		}
		
		location /api {
			proxy_pass http://localhost:8000;
			proxy_set_header Host $host;
		}
	}

}

html 页面部分

然后是前端页面,只有一个按钮就是向服务器发送请求得到新闻和评论:
在这里插入图片描述

整个页面只有这一个按钮。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My first html with ajax</title>
    <style>
        .btn01 {
            display: block;
            width: 160px;
            height: 90px;
        }
    </style>
</head>

<body>
    <button class="btn01">
        click here to send ajax request
    </button>
</body>

</html>

封装ajax请求函数

然后封装了一个发送ajax请求的函数:
其中,obj2query是将query封装成字符串添加到URL里发给服务器,myAjax接受URL,queryobj(query对象),timeout(如果超过这个时间没有接收到响应则 abort 掉这个ajax请求,而success和error参数则分别是成功连接到服务器和连接失败的回调函数。

const obj2Query = (queryObj) => { //将query对象封装成字符串的函数
    // 兼容IE5,6,加入时间戳使得每次query都不一样
    queryObj.t = Date.now()
    let temp = []
    for (key in queryObj) {
        temp.push(`${encodeURIComponent(key)}=${encodeURIComponent(queryObj[key])}`)
    }
    return temp.join('&')
}

const myAjax = (url, queryObj, timeout, success, error) => {
    //0. 解析出完整的url
    queryObj = queryObj || {} //防止queryObj为undefined
    url += '?' + obj2Query(queryObj)
    console.log("myAjax", url)
    //1. 创建一个ajax请求
    let timer
    const xmlHttp = new XMLHttpRequest()
    //2. 打开请求,并设置ajax请求的格式
    xmlHttp.open('GET', url, true)
    //3. 向服务器发送ajax请求
    xmlHttp.send()
    //4. 监听ajax请求的状态变化
    xmlHttp.onreadystatechange = () => {
        if (xmlHttp.readyState == 4) {
            //if response, stop the timeout
            clearTimeout(timer)
            if (xmlHttp.status >= 200 && xmlHttp.status <= 300 || xmlHttp.status === 304) {
                console.log("get server response successfully")
                success(JSON.parse(xmlHttp.responseText))
            } else {
                console.log(`Didn't get the response,status code:${xmlHttp.status}`)
                error(xmlHttp)
            }
        }
    }
    //5. 设置超时
    if (timeout) { //如果传入了指定的timeout
        timer = setTimeout(() => {
            console.log('response timeout')
            xmlHttp.abort()
        }, timeout)
    }
}

设置一个函数,函数中创建一个promise对象,promise中调用刚刚写好的myAjax函数,然后将这个promise对象返回出去。

function getNewsAndComments(url, queryObj, timeout) {
    const promise = new Promise((resolve, reject) => {
        myAjax(url, queryObj, timeout,
            (res) => {
                resolve(res)
            },
            (err) => {
                reject("暂时没有新闻内容")
            })
    })
    return promise
}

纯promise 实现异步读取新闻和评论

然后相应地在前端页面部分,调用上面定义好的 getNewsAndComment 函数:

    <script>
        window.onload = () => {
            let btn01 = document.querySelector('.btn01')
            btn01.onclick = () => {
                const url = "http://localhost:8080/api"
                let queryObj = { //由于是假数据,这个queryObj其实是非必要的
                    id: "42891"
                }
				//先获取新闻页面
                getNewsAndComments(url+'/getNews', {}, 3000).then(
                    (value) => {
                    	//获取新闻页面成功了之后,再封装新的URL,根据这个url去获取评论数据
                        const newUrl = `${url}${value.commentURL}`
                        return getNewsAndComments(newUrl, {}, 3000)
                    },
                    (reason) => {
                        reject("获取新闻失败!")
                    }
                ).then(
                    (value) => {
                        alert(value)
                    }
                )
            }
        }
    </script>

async和await 实现异步读取新闻和评论

或者可以用async和await,用上面定义好的getNewsAndComments来异步获取新闻评论

                const sendXml = async ()=>{
                    let result = await getNewsAndComments(url+'/getNews',{},3000).catch(console.log("获取新闻失败!"))
                    result = await getNewsAndComments(url+result.commentURL)
                    console.log(result)
                }
                sendXml()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值