前后端分离项目
以前的项目:博客系统
这个项目是传统的开发方式,没有实现前后端分离。比较简单,没有跨域等问题。
在传统的开发方式中:界面中事先没有任何东西,用户输入网页地址之后,页面的内容和数据都是服务器返回的。
在前后端分离的开发方式中:页面是提前写好,数据通过Ajax请求而来的。
前后端分离项目
1.前端项目:myblog-pc,使用 vue-cli 搭建。vue create myblog-pc
后端项目:myblog-rd,使用koa-generator创建(koa脚手架)
koa-generator
安装:npm install -g koa-generator
创建项目:koa2 myblog-rd
安装依赖:npm install
2.启动两个项目
myblog-pc:myblog-pc>npm run serve
myblog-rd:myblog-rd>npm start
myblog-rd项目启动之后,在网页中输入localhost:3000,会有服务器返回的值,表示服务器启动成功。
将前端项目和后端项目之间关联起来:前端点击按钮,后端传过来一个数据。
3.下载axiosnpm install --save axios vue-axios
axios
引入代码到vue项目的 main.js 文件中。
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
4.使用 axios
getData(){
this.$http.get("http://localhost:3000").then((response) => {
console.log(response.data)
})
}
默认访问到后端中的根路由
router.get('/', async (ctx, next) => {
await ctx.render('index', {
title: 'Hello Koa 2!'
})
})
5.访问根路由的时候,希望服务器可以返回一个值,而不是渲染一个页面。
await ctx.render('index', {
title: 'Hello Koa 2!'
})
改为
router.get('/', async (ctx, next) => {
ctx.body = "hello serve"
})
其中 ctx.body 就是服务端的返回值。
这时候在前端的 response 值就是服务器的返回值。
6.点击按钮之后会出现跨域问题
7.跨域问题的解决
- 访问控制允许设为 * 跨域
操作: 将下面的代码复制在后端项目的 app.js 中
// 处理跨域
app.use(async (ctx, next) => {
ctx.set("Access-Control-Allow-Origin", "*")
// 继续执行下一个中间件
await next()
})
这个方法不安全。
- CORS
阮一峰cors
npm koa-cors
安装:npm install koa-cors
在后端 app.js 中引入代码:
var cors = require('koa-cors');
app.use(cors());
8.两种请求
CORS是一个W3C标准,是跨域资源共享,克服了AJAX只能同源使用的限制。
使用的时候会有两个方式。
- 简单请求
getData(){
this.$http.get("http://localhost:3000",{
name:'lisi',
age:23
})
.then((response) => {
console.log(response)
})
}
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 。
但是大部分的前后端项目中,数据的传递都需要Content-Type字段的类型是application/json
2) 非简单请求
换成 post 请求
getData(){
this.$http.post("http://localhost:3000",{
name:'lisi',
age:23
})
.then((response) => {
console.log(response)
})
}
预检请求:测试服务器是否允许当前的域访问
正式请求:正式请求
两个请求会增加服务器的压力。
9.token
安装 jsonwebtoken token
生成和校验令牌的中间件。
10.在前端项目中,创建 / 和 /login 两个页面。
在 login 页面中,输入密码账号,点击按钮,执行 doLogin 方法
doLogin() {
this.$http
.post("http://localhost:3000/user/login", {
username: this.username,
password: this.password,
})
.then((res) => {
if (res.data == "success") {
this.$router.push('/');
} else {
alert('用户名或密码不正确!');
}
});
},
访问"http://localhost:3000/user/login"
路由,在服务器中 users.js 中
doLogin() {
this.$http
.post("http://localhost:3000/users/login", {
username: this.username,
password: this.password,
})
.then((res) => {
if (res.data == "success") {
this.$router.push('/');
} else {
alert('用户名或密码不正确!');
}
});
},
前端输入账号密码,传递给后端,后端验证。
router.prefix('/users')
router.post('/login', function (ctx, next) {
let { username,password } = ctx.request.body
if(username == "lisi" && password == "123456"){
ctx.body = "success"
}else{
ctx.body = "fail"
}
})
前端进入主页面之后,后端的数据传递给前端。
// 路由前缀
router.prefix('/blog')
router.get('/list', async (ctx, next) => {
let blogs = [
{
blogId:11,
title:"标题111",
content:"内容111",
postTime:new Date()
},
{
blogId:22,
title:"标题222",
content:"内容222",
postTime:new Date()
},
{
blogId:33,
title:"标题333",
content:"内容333",
postTime:new Date()
}
]
// 返回数据数组
ctx.body = {
blogs
}
前端:
data(){
return {
blogList:[]
}
},
created(){
this.getData()
},
methods: {
getData(){
this.$http.get("http://localhost:3000/blog/list")
.then((res) => {
let { blogs } = res.data;
// 将后端的数据传递给页面
this.blogList = blogs
})
}
}
但是现在的问题是直接进入主页面,还是可以看到数据。
11. token的流程:
12.token的实现
登录成功之后,就生成token.
在 users,js 里面引入 jsonwebtoken
var jwt = require('jsonwebtoken');
let token = jwt.sign({ foo: 'bar' }, 'shhhhh');
jwt.sign()
是token指令的生成算法。{ foo: 'bar' }
一般是账号,密码,或Id的验证信息。'shhhhh'
是随机写的,越复杂越好,将生成的 token 返回给客户端。
if(username == "lisi" && password == "123456"){
// 登录成功
// 生成 token
let payload = {
userId:Math.random(),
username,
}
let token = jwt.sign(payload, '***my_scrret***');
ctx.body = {
status:"success",
token
}
}
token 存放在 localstorage 中或者 vuex 中。
- 在 vuex 中
export default new Vuex.Store({
state: {
token:""
},
// 同步
mutations: {
},
// 异步
actions: {
setToken: (state,token) => {
state.token = token;
localStorage.setItem("mytoken",token)
}
},
modules: {
}
})
在页面中执行this.$store.dispatch('setToken', token)
可以调用 vuex 中的方法。
再次发送请求的时候,就要携带 token。
getData(){
this.axios({
url: "http://localhost:3000/blog/list",
headers: {
"Authorization": localStorage.getItem('mytoken')
}
}).then((res) => {
let {blogs} = res.data;
this.blogList = blogs;
});
}
检验token
async (ctx, next) => {
if(ctx.header.authorization){
let token = ctx.header.authorization;
try {
// 验证token
jwt.verify(token, '***my_scrret***');
await next();
} catch(err) {
// err
ctx.status = 401;
ctx.body = "token不存在或已过期";
}
}
},
如果检验通过,就会执行下一个中间件,返回服务器中的数据。
在页面中渲染数据。
可以设置token的有效时间:let token = jwt.sign(payload, '***my_scrret***', {expiresIn: 10});