前言
在 上一篇文章中,我们探讨了如何利用 Nginx 和 Node.js 两种不同的方法,将静态网站部署到云服务器上。然而,一个完整的网站通常需要与后端服务进行交互,并且需要绑定域名以便用户访问。在本文中,我们将深入讲解如何在云服务器上配置网站,以便它能够与后端服务顺畅交互,并完成域名绑定的过程。
源码地址:https://github.com/baburwang/web-deploy-demo-2.git
项目准备
为了进行本次演示,我们需要准备两个项目:一个代表后端服务,另一个代表前端应用。,具体如下:
后端项目
首先,我们需要创建一个后端项目,这个项目将作为网站的后端服务,负责接收并处理来自前端的请求。
mkdir server
cd server
npm init -y
npm install express
vim index.js
index.js
文件的内容相对直接,它使用 Express 框架来启动一个 Web 服务,并能够处理以 /login 开头的 HTTP 请求。
const express = require('express');
const app = express();
app.use(express.json());
app.post('/login', (req, res) => {
const { username = '', password = '' } = req.body;
if (username !== 'admin') {
res.send({
status: {
code: 1,
message: `${username} 用户不存在`
}
});
return;
}
if (password !== '123456') {
res.send({
status: {
code: 2,
message: `密码错误`
}
});
return;
}
res.send({
status: {
code: 0,
message: '',
},
result: {
id: 1,
username: 'admin',
}
});
});
app.listen(8080, () => {
console.log('Server running at http://localhost:8080')
});
nohup node index.js &
我们将后端项目文件上传到云服务器,并启动服务。随后,我们可以使用 Postman 这类工具来测试接口,确保其正常工作。
前端项目
接下来,我们需要准备一个前端项目。在初始化前端项目之后,我们将编写一个用户登录的组件,代码如下:
npm create vue@latest
<template>
<div class="login-form-wrapper">
<div class="login-form-header">
<h1>密码登录</h1>
</div>
<div class="login-form-body">
<form action="/login" method="post">
<div class="form-item">
<input v-model="username" type="text" placeholder="输入账号" name="username" required>
</div>
<div class="form-item">
<input v-model="password" type="password" placeholder="输入登录密码" name="password" required>
</div>
</form>
</div>
<div class="login-form-footer">
<button @click="loginHandler">登录</button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
const username = ref<String>('');
const password = ref<String>('');
const loginHandler = () => {
console.log('username', username);
console.log('password', password);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://43.139.228.197:8080/login', true);
xhr.responseType = 'json';
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('Response:', xhr.response);
} else {
console.error('Request failed with status:', xhr.status);
}
};
xhr.onerror = function () {
console.error('Network error');
};
xhr.send(JSON.stringify({
username: username.value,
password: password.value,
}));
};
</script>
<style lang="less" scoped>
.login-form-wrapper {
text-align: center;
width: 300px;
height: 300px;
border-radius: 10px;
box-shadow: 0 .46875vw 1.458333vw .416667vw rgba(0,0,0,.05),0 .3125vw .833333vw rgba(0,0,0,.08),0 .15625vw .3125vw -.208333vw rgba(0,0,0,.12);
padding: 20px 0px;
.login-form-header {
h1 {
color: #262626 !important;
font-size: 1.458333vw;
font-weight: 500;
line-height: 1.875vw;
text-align: center;
}
}
.login-form-body {
margin: 20px 20px;
input {
height: 50px;
width: 100%;
background-color: #eee;
border: 1px solid transparent;
border-radius: 10px;
padding: 0 20px;
}
.form-item + .form-item {
margin-top: 10px;
}
}
.login-form-footer {
button {
font-size: 1.041667vw;
font-weight: 500;
height: 2.708333vw;
margin: 0 auto;
width: 14.0625vw;
box-shadow: none;
border-radius: 0.625vw;
padding-left: 1.041667vw;
padding-right: 1.041667vw;
text-shadow: none;
background: #262626;
border-color: #262626;
color: #fff;
border: 1px solid #d9d9d9;
}
}
}
</style>
开发环境
我们首先在本地开发环境中测试登录功能,当点击登录按钮时,浏览器控制台输出:Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
,这是很明显的一个跨域问题。因为我们的前端项目的地址为:http://localhost:5173
,而接口请求地址为:http://43.139.228.197:8080
,它们的域名和端口都不相同,因此触发了浏览器的同源策略限制。
需要明确的是,跨域是浏览器的行为,尽管服务端已经正常返回了响应结果,但浏览器出于安全考虑,阻止了网页读取这些结果。
为了在开发过程中解决接口访问的跨域问题,我们通常会利用开发服务器(devServer)的代理功能。通过配置 devServer,我们可以将前端请求代理到后端服务器,从而绕过浏览器的同源策略限制。这样,在开发阶段,我们就可以正常访问后端接口并进行调试。
1、首先,我们需要修改前端代码中的 XHR 请求地址。我们不应该直接访问后端服务器的地址 http://43.139.228.197:8080/login
,而是应该将其替换为 /api/login
。这样才能被devServer 所拦截。
const loginHandler = () => {
...
xhr.open('POST', '/api/login', true);
...
};
2、devServer 配置中代理 /api
开始的 URL 请求:
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
'/api': {
target: 'http://43.139.228.197:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
}
}
}
})
这里我们简单说下为什么使用 devServer 就不会有跨域问题?
1、首先,当点击登录时,浏览器会发送请求,此时的请求地址为 http://localhost:5173/api/login
,这个请求地址中的协议、IP以及端口与网页是一致的,所以浏览器认为不存在跨域。
2、由于 http://localhost:5173
为 devServer 进程,devServer 在进程中有监听到对自己的接口请求并且是以 /api
开头的 URL,那么 devServer 则会去请求真实的后端服务然后再将响应结果返回的给前端中。
通过这样的方式,我们就解决了开发环境下的跨域请求问题。
生产环境
在生产环境中处理跨域请求需要采取与开发环境不同的策略,因为开发环境下配置的 devServer 不会在生产环境中发挥作用。
处理生产环境下的跨域请求有如下几种方式:
1、Nginx
2、Node
3、Nginx + Node
4、服务端配置 CORS(很多情况下,后端不会配置 CORS,这里略且不表)
Nginx
单纯使用 Nginx 的话,Nginx 不仅会负责托管网页,还会作为代理服务器处理接口请求。这种配置允许前端应用与后端服务之间的交互看起来像是同源的,从而绕过浏览器的跨域限制。
首先构建好前端静态资源后,并上传到云服务器下。
接着,修改 nginx 的配置文件/etc/nginx/nginx.conf
,并且重启 nginx。
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /home/lighthouse/web-depleoy-demo-2/web/dist;
location /api/ {
rewrite "^/api/(.*)$" /$1 break;
proxy_pass http://43.139.228.197:8080; # 后端服务器地址和端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
access_log /var/log/nginx/access.log;
}
1、/home/lighthouse/web-depleoy-demo-2/web/dist
项目构建后的目录。
2、location /api/
则为代理配置,当URL以 /api/
开始时,则会转发到 http://43.139.228.197:8080
。
Node
单纯使用 Node 的话,则是由 Node 托管网页,并且作为代理服务器处理接口请求。
首先,我们启动一个 web 服务进程,用来托管静态资源,同时,该 web 服务进程还可以进行接口代理。
mkdir server
cd server
npm init -y
npm install express
npm install http-proxy-middleware
vim app.js
app.js
的内容如下,分别实现了网页托管以及接口代理。
const path = require('path');
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// 静态资源托管
app.use(express.static(path.resolve(__dirname, 'dist')));
// 接口代理
app.use('/api', createProxyMiddleware({
target: 'http://43.139.228.197:8080',
pathRewrite: {
'^/api': '' // 重写路径,去除 /api 前缀
},
changeOrigin: true
}));
app.listen(3000, () => {
console.log(`Server is start, port is 3000`);
});
Nginx + Node
结合使用 Nginx 和 Node.js 是一种更为推荐的部署方式,Nginx 具有非常强大的处理性能,并且通过 Node 可以具有更加灵活的实现。
- Nginx 负责接口代理
- Node 负责静态资源托管
具体实现方案如下,用户访问的入口为 Nginx 所在的进程,Nginx 不仅会负责接口代理,还会将前端资源的访问请求代理到托管前端资源的服务下。
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# root /usr/share/nginx/html;
# root /home/lighthouse/web-depleoy-demo-2/web/dist;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location /api/ {
rewrite "^/api/(.*)$" /$1 break;
proxy_pass http://43.139.228.197:8080; # 后端服务器地址和端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://43.139.228.197:3000; # 前端web服务器托管地址和端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
access_log /var/log/nginx/access.log;
}
这部分演示代码已上传至 github,可 clone 下来之后直接运行。
git clone https://github.com/baburwang/web-deploy-demo-2.git
cd web-deploy-demo-2
sh deploy.sh
域名绑定
域名绑定的流程相对比较简单,我们在购买后域名后,直接在云服务器选择域名解析,可以通过域名访问了。
需要注意的是,如果没有完成域名备案,访问时可能会被拦截。
总结
通过这两篇文章,想必你一定对网站的部署有了更为深刻的认识。如果你对于以上内容以及其他前端技能存在困惑,欢迎在评论区留言或添加我的个人微信交流。