网关配置
spring:
cloud:
gateway:
routes:
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/?(?<segment>.*),/renren-fast/$\{segment}
跨域
指浏览器不能执行其他网站的脚本,这是由浏览器的同源策略决定的,是浏览器对javascript施加的安全限制。
同源策略:
指协议、域名、端口都要相同,其中一个不同就会产生跨域问题。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
配置类
@Configuration
public class GulimallCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedOriginPattern("*");
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(urlBasedCorsConfigurationSource);
}
}
OSS 整合
依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>aliyun-oss-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.4.5</version>
</dependency>
alibaba:
cloud:
access-key: ***
secret-key: ***
oss:
endpoint: oss-cn-beijing.aliyuncs.com
bucket: ***
@RefreshScope
@RestController
public class OssControler {
@Autowired
OSS ossClient;
@Value("${alibaba.cloud.oss.endpoint}")
private String endpoint;
@Value("${alibaba.cloud.oss.bucket}")
private String bucket;
@Value("${alibaba.cloud.access-key}")
private String accessId;
@RequestMapping("/getendpoint")
public String getEndpoint(){
return endpoint;
}
@RequestMapping("/oss/policy")
public R policy(){
// 填写Host地址,格式为https://bucketname.endpoint。
String host = "https://"+bucket+"."+endpoint;
// 设置上传回调URL,即回调服务器地址,用于处理应用服务器与OSS之间的通信。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。
//String callbackUrl = "https://192.168.0.0:8888";
// 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
String format = new SimpleDateFormat("yyy-MM-dd").format(new Date());
String dir = format+"/";
Map<String, String> respMap = null;
try {
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<String, String>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
//respMap.put("expire", formatISO8601Date(expiration));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
}
return R.ok().put("data",respMap);
}
}
<template>
<div>
<el-upload
action="https://oss地址"
:data="dataObj"
list-type="picture"
:multiple="false" :show-file-list="showFileList"
:file-list="fileList"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:on-preview="handlePreview">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="fileList[0].url" alt="">
</el-dialog>
</div>
</template>
<script>
import {policy} from './policy'
import { getUUID } from '@/utils'
export default {
name: 'singleUpload',
props: {
value: String
},
computed: {
imageUrl() {
return this.value;
},
imageName() {
if (this.value != null && this.value !== '') {
return this.value.substr(this.value.lastIndexOf("/") + 1);
} else {
return null;
}
},
fileList() {
return [{
name: this.imageName,
url: this.imageUrl
}]
},
showFileList: {
get: function () {
return this.value !== null && this.value !== ''&& this.value!==undefined;
},
set: function (newValue) {
}
}
},
data() {
return {
dataObj: {
policy: '',
signature: '',
key: '',
ossaccessKeyId: '',
dir: '',
host: '',
// callback:'',
},
dialogVisible: false
};
},
methods: {
emitInput(val) {
this.$emit('input', val)
},
handleRemove(file, fileList) {
this.emitInput('');
},
handlePreview(file) {
this.dialogVisible = true;
},
beforeUpload(file) {
let _self = this;
return new Promise((resolve, reject) => {
policy().then(response => {
console.log("响应的数据",response);
_self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessid;
_self.dataObj.key = response.data.dir +getUUID()+'_${filename}';
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
console.log("响应的数据222。。。",_self.dataObj);
resolve(true)
}).catch(err => {
reject(false)
})
})
},
handleUploadSuccess(res, file) {
console.log("上传成功...")
this.showFileList = true;
this.fileList.pop();
this.fileList.push({name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name) });
this.emitInput(this.fileList[0].url);
}
}
}
</script>
<style>
</style>
import http from '@/utils/httpRequest.js'
export function policy() {
return new Promise((resolve,reject)=>{
http({
url: http.adornUrl("/thirdparty/oss/policy"),
method: "get",
params: http.adornParams({})
}).then(({ data }) => {
resolve(data);
})
});
}
<template>
<div>
<el-upload
action="http"
:data="dataObj"
:list-type="listType"
:file-list="fileList"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:on-preview="handlePreview"
:limit="maxCount"
:on-exceed="handleExceed"
:show-file-list="showFile"
>
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt />
</el-dialog>
</div>
</template>
<script>
import { policy } from "./policy";
import { getUUID } from '@/utils'
export default {
name: "multiUpload",
props: {
//图片属性数组
value: Array,
//最大上传图片数量
maxCount: {
type: Number,
default: 30
},
listType:{
type: String,
default: "picture-card"
},
showFile:{
type: Boolean,
default: true
}
},
data() {
return {
dataObj: {
policy: "",
signature: "",
key: "",
ossaccessKeyId: "",
dir: "",
host: "",
uuid: ""
},
dialogVisible: false,
dialogImageUrl: null
};
},
computed: {
fileList() {
let fileList = [];
for (let i = 0; i < this.value.length; i++) {
fileList.push({ url: this.value[i] });
}
return fileList;
}
},
mounted() {},
methods: {
emitInput(fileList) {
let value = [];
for (let i = 0; i < fileList.length; i++) {
value.push(fileList[i].url);
}
this.$emit("input", value);
},
handleRemove(file, fileList) {
this.emitInput(fileList);
},
handlePreview(file) {
this.dialogVisible = true;
this.dialogImageUrl = file.url;
},
beforeUpload(file) {
let _self = this;
return new Promise((resolve, reject) => {
policy()
.then(response => {
console.log("这是什么${filename}");
_self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessid;
_self.dataObj.key = response.data.dir +getUUID()+"_${filename}";
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
resolve(true);
})
.catch(err => {
console.log("出错了...",err)
reject(false);
});
});
},
handleUploadSuccess(res, file) {
this.fileList.push({
name: file.name,
// url: this.dataObj.host + "/" + this.dataObj.dir + "/" + file.name; 替换${filename}为真正的文件名
url: this.dataObj.host + "/" + this.dataObj.key.replace("${filename}",file.name)
});
this.emitInput(this.fileList);
},
handleExceed(files, fileList) {
this.$message({
message: "最多只能上传" + this.maxCount + "张图片",
type: "warning",
duration: 1000
});
}
}
};
</script>
<style>
</style>
2020.7之后 validation 不是默认配置了,新版的需要引入spring-boot-starter-validation依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.7.0</version>
</dependency>
再实体类中加入校验注解
@NotBlank(message = "品牌名不能为空")
private String name;
在controller中增加校验@Valid
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand , BindingResult result){
if(result.hasErrors()){
Map<String,String> map = new HashMap();
result.getFieldErrors().forEach((item)->{
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field,message);
});
return R.error(400,"输入的数据不合法").put("data",map);
}else{
brandService.save(brand);
}
return R.ok();
}
校验方法2
分组校验
//java Bean
package com.wzq.gulimall.product.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.rmi.MarshalException;
import java.util.Date;
import com.wzq.common.validator.group.AddGroup;
import com.wzq.common.validator.group.ListValue;
import com.wzq.common.validator.group.UpdateGroup;
import com.wzq.common.validator.group.UpdateStatusGroup;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.*;
/**
* 品牌
*
* @author wzq
* @email wenzhiqiang462@gmail.com
* @date 2022-06-04 20:38:43
*/
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@NotNull(message = "修改商品时必须指定ID",groups = {UpdateGroup.class, UpdateStatusGroup.class})
@Null(message = "新增商品时不用输入ID",groups = {AddGroup.class})
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名不能为空",groups = {UpdateGroup.class,AddGroup.class})
private String name;
/**
* 品牌logo地址
*/
@NotBlank(message = "新增时不能为空",groups = {AddGroup.class})
@URL(message = "输入合法的url地址",groups = {AddGroup.class,UpdateGroup.class})
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@NotNull(groups = {AddGroup.class,UpdateStatusGroup.class})
@ListValue(values = {0,1},groups = {AddGroup.class,UpdateStatusGroup.class})
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty(message = "新增时不能为空",groups = {AddGroup.class})
@Pattern(regexp = "^[a-zA-Z]$",message = "输入a-z或A-Z之间的英文字母",groups = {AddGroup.class,UpdateGroup.class})
private String firstLetter;
/**
* 排序
*/
@NotNull(message = "新增时不能为空",groups = {AddGroup.class})
@Min(value = 0,message = "输入的最小值为0",groups = {AddGroup.class,UpdateGroup.class})
private Integer sort;
}
创建接口类,以实现分组校验
在controller层中,为方法加入@Validated
注解,以及分组接口
@RequestMapping("/update/status")
public R updateStatus(@Validated({UpdateStatusGroup.class}) @RequestBody BrandEntity brand){
brandService.updateById(brand);
return R.ok();
}
自定义校验规则
创建注解类
package com.wzq.common.validator.group;
import com.wzq.common.validator.ListValueConstraintValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "{com.wzq.common.validator.group.ListValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] values() default { };
}
创建校验器
@Constraint(validatedBy = { ListValueConstraintValidator.class })
package com.wzq.common.validator;
import com.wzq.common.validator.group.ListValue;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
//constraintAnnotation.values():注解上定义的值
@Override
public void initialize(ListValue constraintAnnotation) {
int[] values = constraintAnnotation.values();
for (int value : values) {
set.add(value);
}
}
//value:用户输入的值
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
VUE 父子组件传递数据
$on是绑定事件,$emit是触发事件,$off是删除事件
子组件向父组件传递数据–事件机制
子组件给父组件发送一个事件,携带参数
例如:
this.$emit("事件名称",参数列表...)
1、npm install --save pubsub-js
2、在src下的main.js中引用:
① import PubSub from 'pubsub-js'
② Vue.prototype.PubSub = PubSub
Object 划分
**1.PO(persistant object) 持久对象**
PO 就是对应数据库中某个表中的一条记录,多个记录可以用 PO 的集合。 PO 中应该不包含任何对数据库的操作。
**2.DO(Domain Object)领域对象**
就是从现实世界中抽象出来的有形或无形的业务实体。
**3.TO(Transfer Object) ,数据传输对象**
不同的应用程序之间传输的对象
**4.DTO(Data Transfer Object)数据传输对象**
这个概念来源于 J2EE 的设计模式,原来的目的是为了 EJB 的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,泛指用于展示层与服务层之间的数据传输对象。
**5.VO(value object) 值对象**
通常用于业务层之间的数据传递,和 PO 一样也是仅仅包含数据而已。但应是抽象出的业务对象 , 可以和表对应 , 也可以不 , 这根据业务的需要 。用 new 关键字创建,由GC 回收的。
View object:视图对象
接受页面传递来的数据,封装对象将业务处理完成的对象,封装成页面要用的数据
**6.BO(business object) 业务对象**
从业务模型的角度看 , 见 UML 元件领域模型中的领域对象。封装业务逻辑的 java 对象 , 通过调用 DAO 方法 , 结合 PO,VO 进行业务操作。business object: 业务对象 主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。 比如一个简历,有教育经历、工作经历、社会关系等等。 我们可以把教育经历对应一个 PO ,工作经历对应一个 PO ,社会关系对应一个 PO 。 建立一个对应简历的 BO 对象处理简历,每个 BO 包含这些 PO 。 这样处理业务逻辑时,我们就可以针对 BO 去处理
**7.POJO(plain ordinary java object) 简单无规则 java 对象**
传统意义的 java 对象。就是说在一些 Object/Relation Mapping 工具中,能够做到维护数据库表记录的 persisent object 完全是一个符合 Java Bean 规范的纯 Java 对象,没有增加别的属性和方法。我的理解就是最基本的 java Bean ,只有属性字段及 setter 和 getter方法!
POJO 是 DO/DTO/BO/VO 的统称。
**8.DAO(data access object) 数据访问对象**
是一个 sun 的一个标准 j2ee 设计模式, 这个模式中有个接口就是 DAO ,它负持久层的操作。为业务层提供接口。此对象用于访问数据库。通常和 PO 结合使用, DAO 中包含了各种数据库的操作方法。通过它的方法 , 结合 PO 对数据库进行相关的操作。夹在业务逻辑与数据库资源中间。配合 VO, 提供数据库的 CRUD 操作。
springboot2.6开始,默认禁止循环依赖
不鼓励依赖循环引用,默认情况下禁止使用循环引用。更新应用程序以消除bean之间的依赖循环。作为最后手段,有可能解决自动循环通过设置 spring.main.allow-circular-references to true