最近在练习前后端联调的时候,做了一个练习:
localhost:8001
的ajax 向后端服务器localhost:8000
发送一个路由为api/getNews
的请求,服务器返回 阿美利加大统领得感冒的新闻,并在返回对象中带了一个commentsURL
路由对象。?id=123&time=124124
这样的query
部分,而是简单地根据路由返回的假数据。下面是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 ,那么返回“卧槽这老头太牛逼了!”的评论。
由于我们的页面和服务器都是在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 lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My first html with ajaxtitle>
<style>
.btn01 {
display: block;
width: 160px;
height: 90px;
}
style>
head>
<body>
<button class="btn01">
click here to send ajax request
button>
body>
html>
然后封装了一个发送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
}
然后相应地在前端页面部分,调用上面定义好的 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,用上面定义好的getNewsAndComments来异步获取新闻评论
const sendXml = async ()=>{
let result = await getNewsAndComments(url+'/getNews',{},3000).catch(console.log("获取新闻失败!"))
result = await getNewsAndComments(url+result.commentURL)
console.log(result)
}
sendXml()