一、简介
node.js版本:v12.16.3
上传头像接口,图片存储在七牛云中,注册并实名认证之后可以免费使用
以下会描述使用vue搭建项目框架的过程,不涉及到具体页面的编写等,有兴趣的可以看一下,没兴趣的可以直接跳过,直接从github上克隆代码运行。
二、创建web项目
创建完成后进行相关配置
三、安装vue脚手架工具
3.1 首先在idea的Terminal中输入以下命令安装淘宝的npm镜像,这样下载速度快
执行以下命令
npm i -g cnpm --registry=https://registry.npm.taobao.org
idea中执行“npm”命令,提示'node' 不是内部或外部命令,也不是可运行的程序
3.2 开始安装vue脚手架工具
执行以下命令
cnpm i -g vue-cli
测试是否安装成功
vue -V
四、构建vue项目
执行以下命令
vue init webpack shop-project-web
分别执行上图中的命令
ok,构建完成。
五、安装iview并在项目引入(可以快速搭建页面)
首先先停止vue项目(在控制台按Ctrl+C,再按Y停止项目),停止以后执行以下命令
cnpm install view-design --save
在main.js中加入iview
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
//引入iview
import ViewUI from 'view-design';
import 'view-design/dist/styles/iview.css';
Vue.use(ViewUI);
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
ok,到这里我们就可以通过使用iview来快速搭建页面,这里不再详述页面的搭建过程等。
六、安装axios并在项目引入(便于实现ajax请求)
首先先停止vue项目(在控制台按Ctrl+C,再按Y停止项目),停止以后执行以下命令
cnpm install axios -S
在main.js中加入axios
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
//引入iview
import ViewUI from 'view-design';
import 'view-design/dist/styles/iview.css';
Vue.use(ViewUI);
//引入axios
import axios from 'axios'
Vue.prototype.$axios = axios
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
七、安装七牛云js用于实现客户端图片上传
cnpm i qiniu-js
八、安装缓存插件用于缓存数据
cnpm install good-storage
九、vue项目介绍
vue架构里各个部分的作用如下图所示
在index.js里配置路由,以此实现页面的跳转
使用axios发出ajax请求
ok,看到这里相信大家对vue已经有了初步的了解,这里不再继续详述页面相关的东西。
十、解决跨域问题
在我们的gateway服务中添加一个配置类即可搞定
package com.liazhan.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* @version:V1.0
* @Description: 解决跨域问题
* @author: Liazhan
* @date 2020/5/5 9:24
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter(){
CorsConfiguration config=new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
//设置预检请求的缓存时间(秒),在这个时间段里,对于相同的跨域请求不会再预检了
config.setMaxAge(18000L);
UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**",config);
return new CorsWebFilter(source);
}
}
十一、使用七牛云实现头像上传
上传流程是客户端向服务端获取一个token,服务端生成token返回(添加同一个ip半小时内最多获取十次token限制),然后客户端用这个token去实现在七牛云上传图片。
shop-common-core添加ip工具类,需要添加servlet依赖
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
package com.liazhan.core.utils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @version:V1.0
* @Description: IP工具类
* @author: Liazhan
* @date 2020/5/7 11:15
*/
public class IPUtil {
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
// = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress="";
}
// ipAddress = this.getRequest().getRemoteAddr();
return ipAddress;
}
}
shop-service-impl-member添加七牛云sdk依赖,修改常量类,添加头像上传接口
<!-- 七牛云 -->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.2.28</version>
</dependency>
package com.liazhan.member.consts;
/**
* @version:V1.0
* @Description: 会员服务常量类
* @author: Liazhan
* @date 2020/4/28 10:11
*/
public interface MemberConst {
//登录token在redis的key前缀
String MEMBER_LOGIN_TOKEN_PREFIX = "login.token";
//登录token过期时间 1小时
Long MEMBER_LOGIN_TOKEN_TIMEOUT = 3600L;
//登陆token失效状态
Integer MEMBER_LOGIN_TOKEN_INVALID = 0;
//登陆token有效状态
Integer MEMBER_LOGIN_TOKEN_VALID = 1;
//七牛云accessKey
String QINIU_ACCESSKEY = "这里输入七牛云accessKey";
//七牛云secretKey
String QINIU_SECRETKEY = "这里输入七牛云secretKey";
//七牛云头像存储空间名称
String QINIU_HEADIMG_BUCKET = "这里输入七牛云存储空间名称";
//用户获取上传头像token的次数在redis的key前缀
String HEADIMG_TOKEN_COUNT_PREFIX = "headimg.token.count";
//用户获取上传头像token的次数在redis的过期时间 半小时
Long HEADIMG_TOKEN_COUNT_TIMEOUT = 1800L;
//用户获取上传头像token的最大次数 10次
Integer HEADIMG_TOKEN_COUNT_MAX = 10;
}
package com.liazhan.member.service;
import com.alibaba.fastjson.JSONObject;
import com.liazhan.base.BaseResponse;
import com.liazhan.member.input.dto.UserRegistInpDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
/**
* @version:V1.0
* @Description: 会员注册相关接口
* @author: Liazhan
* @date 2020/4/24 11:13
*/
@Api(tags = "会员注册相关接口")
public interface MemberRegistService {
@PostMapping("/regist")
@ApiOperation(value = "会员注册接口")
BaseResponse<JSONObject> regist(@RequestBody @Valid UserRegistInpDTO userInpDTO);
@GetMapping("/getUploadHeadImgToken")
@ApiOperation(value = "会员注册获取上传头像token接口")
BaseResponse<JSONObject> getUploadHeadImgToken(HttpServletRequest request);
}
package com.liazhan.member.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.liazhan.base.BaseResponse;
import com.liazhan.base.BaseServiceImpl;
import com.liazhan.base.consts.BaseConst;
import com.liazhan.core.utils.DtoUtil;
import com.liazhan.core.utils.IPUtil;
import com.liazhan.core.utils.RedisUtil;
import com.liazhan.member.consts.MemberConst;
import com.liazhan.member.dao.UserDao;
import com.liazhan.member.dao.entity.UserDO;
import com.liazhan.member.feign.WeiXinVerificationCodeServiceFeign;
import com.liazhan.member.input.dto.UserRegistInpDTO;
import com.liazhan.member.service.MemberRegistService;
import com.qiniu.util.Auth;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
/**
* @version:V1.0
* @Description: 会员注册相关接口实现类
* @author: Liazhan
* @date 2020/4/24 11:18
*/
@RestController
public class MemberRegistServiceImpl extends BaseServiceImpl<JSONObject> implements MemberRegistService {
@Autowired
private WeiXinVerificationCodeServiceFeign weiXinVerificationCodeServiceFeign;
@Autowired
private UserDao userDao;
@Autowired
private RedisUtil redisUtil;
@Override
public BaseResponse<JSONObject> regist(@Valid UserRegistInpDTO userInpDTO) {
//1.校验验证码是否正确
BaseResponse<JSONObject> checkResponse = weiXinVerificationCodeServiceFeign.checkVerificationCode(userInpDTO.getPhone(), userInpDTO.getVerificationCode());
if(!BaseConst.HTTP_RES_CODE_200.equals(checkResponse.getCode())){
return getResultError(checkResponse.getMsg());
}
//2.密码用md5加密
String encodePassword = DigestUtils.md5DigestAsHex(userInpDTO.getPassword().getBytes());
userInpDTO.setPassword(encodePassword);
//3.DTO转do
UserDO userDO = DtoUtil.dtoToDo(userInpDTO, UserDO.class);
//4.保存到数据库
UserDO save = userDao.save(userDO);
return save.getUserId()==null?getResultError("注册失败!"):getResultSuccess("注册成功!");
}
@Override
public BaseResponse<JSONObject> getUploadHeadImgToken(HttpServletRequest request) {
//1.获取请求方ip,判断是否已超过请求次数,若是,则返回操作繁忙,否则次数+1
String ipAddr = IPUtil.getIpAddr(request);
String tokenCount = redisUtil.getString(MemberConst.HEADIMG_TOKEN_COUNT_PREFIX + ipAddr);
if(StringUtils.isBlank(tokenCount)){
redisUtil.setString(MemberConst.HEADIMG_TOKEN_COUNT_PREFIX+ipAddr,
"1",MemberConst.HEADIMG_TOKEN_COUNT_TIMEOUT);
}else{
Integer tokenCountInt = Integer.valueOf(tokenCount);
if(tokenCountInt<MemberConst.HEADIMG_TOKEN_COUNT_MAX){
tokenCountInt++;
redisUtil.setString(MemberConst.HEADIMG_TOKEN_COUNT_PREFIX+ipAddr,
tokenCountInt+"",MemberConst.HEADIMG_TOKEN_COUNT_TIMEOUT);
}else{
return getResultError("图片上传繁忙,请稍后再试!");
}
}
//2.生成token
Auth auth = Auth.create(MemberConst.QINIU_ACCESSKEY, MemberConst.QINIU_SECRETKEY);
String upToken = auth.uploadToken(MemberConst.QINIU_HEADIMG_BUCKET);
JSONObject jsonObject = new JSONObject();
jsonObject.put("token",upToken);
return getResultSuccess(jsonObject);
}
}
ok,最终效果如下
页面还很简陋,登陆还没有添加验证码之类的校验,后续会完善一下。
由于目前还没有编写需要登陆token才能获取数据的接口,因此后续再添加axios响应拦截器,当请求响应token错误失败码时,将自动跳转到登陆页面。
后端项目github地址:https://github.com/liazhan/shop-project/tree/845fd2f6cf4633d1f23ec5d11565737834e177ac
前端项目github地址:https://github.com/liazhan/shop-project-web/tree/d071343165ab5a84c859dc4a8a10d6b72cd657e9
前端项目的运行方式如下:
1、clone下来之后用idea打开,打开Terminal界面
2、输入以下命令安装,若没有CNPM则根据上文提到的方式安装CNPM
cnpm install
3、运行
npm run dev