1. 如何处理好前后端分离的 API 问题
意义很大,但是你的问题本身认识有偏差。
对于前后端分离,认识上有个误区,那就是很多人自称:老早就分离了,全AJAX,使用Angular或者什么什么就可以了。
这个说法是不合适的,打个比方,别人问的是“如何解决家禽把蛋生在水草边的问题?”,但实际上人家养的是鸭子,答题的却是养鸡的,所以回答“不让去水边就行了”,这显然不在点子上。
这两年业界说的前后端分离,是限于偏展示类的系统(用A代替),而不是应用、管控类Web项目(用B代替),在B类项目里,前后端是天然分离的,对此,除了少部分后端开发人员,基本所有人的认识都是一致的。上一段中这样回答的人一般都是只做B类项目,在B类项目里,前后端分离是共识,不需要讨论。
那么,剩下的问题就是讨论A类项目的前后端分离了。这个问题的核心在什么地方呢,在于模板的与数据结合的位置,以及,模板的控制权在谁手里。经过这两年的讨论,基本上我们可以达成的共识就是:模板应当由前端人员去控制,主要原因有两方面:
- 性能优化(尤其是外部资源的管理与发布,请求合并等等)
- 协作的顺畅性(已形成模板的界面片段的返工等问题)
那么,模板到底应该在什么地方跟数据结合?
这个问题就比较折腾了,有部分人尝试像B类项目那样,使用js模板,然后在浏览器端执行,这是存在一些问题的,比如说seo不友好,首屏性能不够,尤其对于首页DOM量很大的电商类网站,差距很明显。
所以还是得把主要的模板放在服务端来执行。在这个过程中,阿里作了一些尝试,那就是引入Node层,在这一层把模板与数据进行合成,然后浏览器拿到的就是生成好的HTML了,但也不是所有HTML都是这么生成好的,还是会有一些内容等到了浏览器之后,再用js去加载和生成。
2. nodejs-koa2(mvc模式)前后端分离 前端设计
前后端分离,前端nodejs运行环境,使用koa2集成负责资源分配与用户交互,实现token验证用户身份,路由控制。等!
自行 网络 解决;
"program": "${workspaceFolder}app.js"
此处就是是将app.js作为启动文件。${workspaceFolder}代表根目录,vsc启动时会在根目录下找到并加载app.js文件。
参数介绍: name 项目名称、 version 版本号、 description 项目描述、 main 项目启动文件、 scripts 启动快捷设置, author 作者, dependencies 第3方中间件名称及版本。
最重要的
“ dependencies ”这里添加一些要用到的包,以上是这次要用到的所有的包,版本自己更改。
“ scripts ”这里是一些nodejs的便捷命令,上线的时候会用到,直接在终端中,package.json同级目录 ,执行‘npm start’ 即 可启动app.js。
别的没啥太大作用瞎写即可。
启动相关配置,封装到config/init.js中,启动文件直接引用即可
3-6-1、init.js项目核心。
异常友好处理方法封装
路由配置
视图渲染
核心集成
3-6-2、config.js项目参数配置。为什么不用json文件 因为json不能加注释
3-6-3、token.js项目token相关方法封装。
执行后项目结构会增加两个文件
新增
src/hello.js。
views/index.html
浏览器访问: http://127.0.0.1:3000/koa/login
输入值获取token
获取的token如图:
先不用带token进行访问: http://127.0.0.1:3000/koa/ hello/jiaobaba,被token拦截,返回401
带上token访问: http://127.0.0.1:3000/koa/ hello/jiaobaba
测试页面渲染,及跳转html页面,直接访问 http://127.0.0.1:3000/koa /views
结束!!!!!!
需要源码联系我
3. 前后端分离的前端是怎么部署到生产环境中的,直接通过 nginx 吗
front-end-separate(前后端分离脚手架)
front-end-separate
一个前后端分离的脚手架工具(自主研发)
为什么选择grunt而不是gulp
如果你也和我一样喜欢grunt这种配置的方式,那么我相信这个脚手架觉对十分适合你
所有静态资源都md5全并压缩打包,css,js,img,html
已在生产环境验证
基于express和grunt的前后端分离框架
模板引擎使用的是nunjucks,好处是可以实现模版继承,又不像jade一样把html标签都简化了
express提供路由服务
项目中app为原代码文件(开发用),dist为打包后的文件(用于线上)
开发使用app,线上使用dist,支持一键cdn部署,加速你的项目
项目启动时,修改任何express代码,可以实现自动重启–基于nodemon
支持sass图片精灵(自动打包精灵图片,再也不用手动去拼凑了)
基于grunt md5 打包合并
线上输出的html已经压缩成一行(让你的代码更有Geeker范)
4. 前后端分离项目——登录Token校验思路
对token的校验分为前端和后端
前端: Vue-Cli 2.x + axios
后端:SpringBoot 2.3.4
这里的话,userToken和userId放到sessionStorage是关键步骤
后端主要是使用拦截器来进行请求的拦截和校验
解释一下思路:
这里的话,针对需要拦截的路径和需要放行的路径进行配置就行
关于redisTemple的引入这里就不再赘述。
到这里为止,前后端的token就都做完了,后面就再讲讲前端的一些其他思路吧
对于登录状态的判断,前端可以在router.foreach上对路由进行状态判定,从而实现页面程度的拦截(具体可以参考最后的参考文章2)
在使用拦截器后,会发现前端部分请求会无法正常到达后端,网络后发现是因为 axios发送正式请求前会先发送一个嗅探请求 ,而嗅探请求是不携带我们封装的header的,所以会导致部分请求会无法成功,解决的方式有很多种,这里的话是选择了在后端去直接处理
参考文章
1、SpringBoot加了拦截器后出现的跨域问题解析
https://blog.csdn.net/mrkorbin/article/details/104066979
2、Vue项目中实现用户登录及token验证
https://www.cnblogs.com/web-record/p/9876916.html
5. 前后端分离构架特点
就是把数据和页面分离开,后端不提供页面,只是纯粹的通过 Web API 来提供数据和业务交互能力,Web 前端就是纯粹的客户端角色,与 WinForm、移动终端应用属于同样的角色,可以把它们合在一起,统称为前端,分离开了后,后端不再考虑页面如何美化,前段也不需要了解后端采用的是什么样的技术实现方案,使得前后端的开发人员能够更加专注于自身业务的开发。
以前的一体式 Web 架构示意
现在的前后端分离构架示意图
前后端分离后,会出现以前web一体式构架中没有出现过得问题,比如认证,会话机制,签名验证等,
既然是做对外的api接口,当然安全问题是我们需要认真考虑的问题了,那么webapi会存在那些安全隐患呢?
处理这些安全隐患可以采用token+signature认证的方式;原理是:(1)做一个认证服务,提供一个认证的webapi,用户先访问它获取对应的token;(2)用户拿着相应的token以及请求的参数和服务器端提供的签名算法计算出签名后再去访问指定的api;(3)服务器端每次接收到请求就获取对应用户的token和请求参数,服务器端再次计算签名和客户端签名做对比,如果验证通过则正常访问相应的api,验证失败则返回具体的失败信息
6. 前后端分离后表单验证是前端还是后端
验证是肯定需要两次的,前端先做,一般都会用正则来匹配是否有非法字符,但同时一般会和数据库对接来验证用户名是否已被注册,但此时依然得先通过前端来筛选是否存在非法字符在与后端数据库进行对接,否则不是浪费资源嘛,因此答案就是
7. 前端和后端分离开发测试怎么整合
既然是分离开发了,那应该不好整合测试。
前端也有用到后端接口,测试前端的时候其实也是在测试后端了。
8. Vue项目前后端分离下的前端鉴权方案
# Vue项目前后端分离下的前端鉴权方案
### 技术栈
前端Vue全家桶,后台.net。
### 需求分析
1. 前端路由鉴权,屏蔽地址栏入侵
2. 路由数据由后台管理,前端只按固定规则异步加载路由
3. 权限控制精确到每一个按钮
4. 自动更新token
5. 同一个浏览器只能登录一个账号
### 前端方案
> 对于需求1、2、3,采用异步加载路由方案
1. 首先编写vue全局路由守卫
2. 排除登录路由和无需鉴权路由
3. 登录后请求拉取用户菜单数据
4. 在vuex里处理菜单和路由匹配数据
5. 将在vuex里处理好的路由数据通过`addRoutes`异步推入路由
```
router.beforeEach((to, from, next) => {
// 判断当前用户是否已拉取权限菜单
if (store.state.sidebar.userRouter.length === 0) {
// 无菜单时拉取
getMenuRouter()
.then(res => {
let _menu = res.data.Data.ColumnDataList || [];
// if (res.data.Data.ColumnDataList.length > 0) {
// 整理菜单&路由数据
store.commit("setMenuRouter", _menu);
// 推入权限路由列表
router.addRoutes(store.state.sidebar.userRouter);
next({...to, replace: true });
// }
})
.catch(err => {
// console.log(err);
// Message.error("服务器连接失败");
});
} else {
//当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的菜单会自动进入404页面
if (to.path == "/login") {
next({
name: "index"
});
} else {
next();
}
}
} else {
// 无登录状态时重定向至登录 或可进入无需登录状态路径
if (to.path == "/login" || to.meta.auth === 0) {
next();
} else {
next({
path: "/login"
});
}
}
});
```
##### 注意
> 我这里无需鉴权的路由直接写在router文件夹下的index.js,通过路由元信息meta携带指定标识
```
{
path: "/err-404",
name: "err404",
meta: {
authentication: false
},
component: resolve => require(["../views/error/404.vue"], resolve)
},
```
> 上面说到路由是根据后台返回菜单数据根据一定规则生成,因此一些不是菜单,又需要登录状态的路由,我写在router文件夹下的router.js里,在上面步骤4里处理后台返回菜单数据时,和处理好的菜单路由数据合并一同通过`addRoutes`推入。
这样做会有一定的被地址栏入侵的风险,但是笔者这里大多是不太重要的路由,如果你要求咳咳,可以定一份字典来和后台接口配合精确加载每一个路由。
```
// 加入企业
{
path: "/join-company",
name: "join-company",
component: resolve => require([`@/views/index/join-company.vue`], resolve)
},
```
> 在vuex中将分配的菜单数据转化为前端可用的路由数据,我是这样做的:
管理系统在新增菜单时需要填写一个页面地址字段`Url`,前端得到后台菜单数据后根据`Url`字段来匹配路由加载的文件路径,每个菜单一个文件夹的好处是:你可以在这里拆分js、css和此菜单私有组件等
```
menu.forEach(item => {
let routerItem = {
path: item.Url,
name: item.Id,
meta: {
auth: item.Children,
}, // 路由元信息 定义路由时即可携带的参数,可用来管理每个路由的按钮操作权限
component: resolve =>
require([`@/views${item.Url}/index.vue`], resolve) // 路由映射真实视图路径
};
routerBox.push(routerItem);
});
```
> 关于如何精确控制每一个按钮我是这样做的,将按钮编码放在路由元信息里,在当前路由下匹配来控制页面上的按钮是否创建。
菜单数据返回的都是多级结构,每个菜单下的子集就是当前菜单下的按钮权限码数组,我把每个菜单下的按钮放在此菜单的路由元信息`meta.auth`中。这样作的好处是:按钮权限校验只需匹配每个菜单路由元信息下的数据,这样校验池长度通常不会超过5个。
```
created() {
this.owner = this.$route.meta.auth.map(item => item.Code);
}
methods: {
matchingOwner(auth) {
return this.owner.some(item => item === auth);
}
}
```
> 需求4自动更新token,就是简单的时间判断,并在请求头添加字段来通知后台更新token并在头部返回,前端接受到带token的请求就直接更新token
```
// 在axios的请求拦截器中
let token = getSession(auth_code);
if (token) config.headers.auth = token;
if (tokenIsExpire(token)) {
// 判断是否需要刷新jwt
config.headers.refreshtoken = true;
}
// 在axios的响应拦截器中
if (res.headers.auth) {
setSession(auth_code, res.headers.auth);
}
```
> 对于需求5的处理比较麻烦,要跨tab页只能通过`cookie`或`local`,笔者这里不允许使用`cookie`因此采用的`localstorage`。通过打开的新页面读取`localstorage`内的`token`数据来同步多个页面的账号信息。`token`使用的`jwt`并前端md5加密。
这里需要注意一点是页面切换要立即同步账号信息。
> 经过需求5改造后的全局路由守卫是这样的:
```
function _AUTH_() {
// 切换窗口时校验账号是否发生变化
window.addEventListener("visibilitychange", function() {
let Local_auth = getLocal(auth_code, true);
let Session_auth = getSession(auth_code);
if (document.hidden == false && Local_auth && Local_auth != Session_auth) {
setSession(auth_code, Local_auth, true);
router.go(0)
}
})
router.beforeEach((to, from, next) => {
// 判断当前用户是否已拉取权限菜单
if (store.state.sidebar.userRouter.length === 0) {
// 无菜单时拉取
getMenuRouter()
.then(res => {
let _menu = res.data.Data.ColumnDataList || [];
// if (res.data.Data.ColumnDataList.length > 0) {
// 整理菜单&路由数据
store.commit("setMenuRouter", _menu);
// 推入权限路由列表
router.addRoutes(store.state.sidebar.userRouter);
next({...to, replace: true });
// }
})
.catch(err => {
// console.log(err);
// Message.error("服务器连接失败");
});
} else {
//当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的菜单会自动进入404页面
if (to.path == "/login") {
next({
name: "index"
});
} else {
next();
}
}
} else {
// 无登录状态时重定向至登录 或可进入无需登录状态路径
if (to.path == "/login" || to.meta.auth === 0) {
next();
} else {
next({
path: "/login"
});
}
}
});
}
```
> 经过需求5改造后的axios的请求拦截器是这样的,因为ie无法使用`visibilitychange`,并且尝试网络其他属性无效,因此在请求发出前做了粗暴处理:
```
if (ie浏览器) {
setLocal('_ie', Math.random())
let Local_auth = getLocal(auth_code, true);
let Session_auth = getSession(auth_code);
if (Local_auth && Local_auth != Session_auth) {
setSession(auth_code, Local_auth, true);
router.go(0)
return false
}
}
```
> 这里有一个小问题需要注意:因为用的`local`因此首次打开浏览器可能会有登录已过期的提示,这里相信大家都能找到适合自己的处理方案
### 结语
经过这些简单又好用的处理,一个基本满足需求的前后端分离前端鉴权方案就诞生啦
9. 怎样用前端代码判断后台输出的一条数据是否有空格并按空格分离成多条数据
var a;//后台获取的数据
var b;//分割后的数组;
if(a && a.indexOf(" ")>-1){
b = a.split(" ");
}
b就是你要获取的分割之后的数组,你在循环遍历他就行了
10. vue是怎么做到前端分离的
前端开发完全分离总结:
1:一个优秀的组件化开发框架(非必须),如vue 理由:自动生成工程目录+实时编译+打包压缩+es6语法检查+stylus解析+自动化单元测试工具kram+etc..
2:一个优秀的跨域解决方案,如vue cli-proxyTable(基于http-proxy-middleware)