<property name="nullCatalogMeansCurrent" value="true"/>
列如
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/ssm?
serverTimezone=UTC"
userId="root"
password="123456yql">
<property name="nullCatalogMeansCurrent" value="true"/>
jdbcConnection>
SET @auto_id = 0;
UPDATE t_emp
SET emp_id = (@auto_id := @auto_id + 1);
ALTER TABLE t_emp
AUTO_INCREMENT = 1;
我们可以把POJO中的实体类命名和数据库表中字段的命名调整一致,但是数据库表字段的命名方式并不是驼峰式命名方式,不符合Java中对于类属性的命名规则,因此不推荐使用。
我们可以通过在表对应的mapper.xml配置文件中通过标签给字段取别名的方式,使得重新命名的表字段别名与实体类中的属性名保持一致,再通过****标签引入sql代码块,这样也可以达到数据正常封装的效果。
<sql id="columns">
p_id as pid,
p_name as pname,
pg_id as pgid
sql>
<select id="queryProduct" resultType="product1">
select <include refid="columns"/> from product where ${column} = #{value}
select>
以下是通过在mybatis配置文件中加入中的logImpl设置为STDOUT_LOGGING是为了使日志输出mybatis生产的sql语句,这样就可以通过日志输出看到在list中拿到了正常封装的数据对象。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
我们可以通过标签来将数据库表中字段映射到对应的实体类属性上,完成数据库查询数据的封装,如果实体类的属性中存在自定义类属性,那么也可以通过resultMap进行数据的层层映射,封装到对应自定义类中的基本类型数据中。
<resultMap id="productName" type="product1">
<id property="pid" column="p_id"/>
<result property="pname" column="p_name"/>
<result property="pgid" column="pg_id"/>
resultMap>
<select id="selectProductById" parameterType="int" resultMap="productName">
select * from product where p_id = #{id}
select>
在Mybatis核心配置文件标签内设置mapUnderscoreToCamelCase属性的值为true,那么数据库就会将表字段的分割式命名(例如:p_id)自动映射到实体类的驼峰式命名(例如:pId)上,且不区分驼峰式命名的大小写。
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
IDEA 无法热加载自动更新,On ‘update’ action:选项里面没有update classes and resources这一选项
1.出现的问题
2.原因: 在Tomcat Deployment选项部署项目选择了只有War包的项目
如果 glyphicons-halflings-regular.eot 加载不出来 就是登录界面图标没出来 看看F12 网络他的请求地址对不对,不对的去改bootstrap/css/bootstrap.css.map 搜索 Glyphicons Halflings 换成这个 (改成自己的路径)
@font-face {
font-family: 'Glyphicons Halflings';
src: url('../fonts/glyphicons-halflings-regular.eot');
src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
}
DELIMITER //
CREATE PROCEDURE insert_data(IN max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
insert into my_table(login_acct, user_pswd, user_name, email) values (CONCAT('asd', i),CONCAT('asd', i),CONCAT('asd', i),CONCAT('asd', i));
UNTIL i = max_num
END REPEAT;
COMMIT;
END
在 Thymeleaf 中,您可以使用条件语句来根据当前页码的位置显示不同数量的分页链接。例如,如果您有一个名为 currentPage
的变量,它表示当前页码,以及一个名为 totalPages
的变量,它表示总页数,则可以这样做:
<ul>
<li th:if="${currentPage == 1}">
<span th:each="page : ${#numbers.sequence(1, 3)}">
<a th:href="@{/search(page=${page})}" th:text="${page}">Pagea>
span>
li>
<li th:if="${currentPage > 1 and currentPage < totalPages}">
<span th:each="page : ${#numbers.sequence(currentPage - 2, currentPage + 2)}">
<a th:href="@{/search(page=${page})}" th:text="${page}">Pagea>
span>
li>
<li th:if="${currentPage == totalPages}">
<span th:each="page : ${#numbers.sequence(totalPages - 2, totalPages)}">
<a th:href="@{/search(page=${page})}" th:text="${page}">Pagea>
span>
li>
ul>
在上面的示例中,我们使用 th:if
属性来判断当前页码的位置。如果当前页码是第一页,则我们使用 th:each="page : ${#numbers.sequence(1, 3)}"
来显示前三页的分页链接。如果当前页码在中间,则我们使用 th:each="page : ${#numbers.sequence(currentPage - 2, currentPage + 2)}"
来显示前后两页共五页的分页链接。如果当前页码是最后一页,则我们使用 th:each="page : ${#numbers.sequence(totalPages - 2, totalPages)}"
来显示最后三页的分页链接。
请注意,您需要根据您的具体需求修改上面的示例,以便指定正确的 URL 和查询参数。
有几种方法可以配置Spring MVC不拦截静态资源。一种方法是在拦截器中添加静态资源的排除(涉及spring-mvc.xml)。另一种方法是使用默认的servlet来处理静态资源(涉及spring-mvc.xml和web.xml)。第三种方法是更改Spring的全局拦截设置,仅拦截以*.do结尾的请求(涉及web.xml)1。
针对这三种方案的优劣分析:第一种方案配置比较臃肿,多个拦截器时增加文件行数,不推荐使用;第二种方案使用默认的Servlet进行资源文件的访问,Spring拦截所有请求,然后再将资源文件交由默认的Sevlet进行处理,性能上少有损耗;第三种方案Spring只是处理以’.do’结尾的访问,性能上更加高效,但是再访问路径上必须都以’.do’结尾,URL不太文雅;综上所述,推荐使用第二和第三中方案1。
第一种方法是在拦截器中添加静态资源的排除(涉及spring-mvc.xml)。在spring-mvc.xml文件中,可以使用mvc:resources来设置静态资源,然后在mvc:interceptors中添加mvc:interceptor,并在其中使用mvc:exclude-mapping来排除静态资源的拦截。例如,可以使用
第二种方法是使用默认的servlet来处理静态资源(涉及spring-mvc.xml和web.xml)。在spring-mvc.xml文件中,可以使用mvc:default-servlet-handler/来启用默认的Servlet。然后,在web.xml文件中,可以添加来增加对静态资源的处理。例如,可以使用*.css来指定默认的Servlet处理所有CSS文件。
第三种方法是更改Spring的全局拦截设置,仅拦截以*.do结尾的请求(涉及web.xml)。在web.xml文件中,可以修改来更改Spring的全局拦截设置。例如,可以使用*.do来指定Spring仅拦截以.do结尾的请求。这样,Spring就只会处理以.do结尾的请求,不再维护静态资源。
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js">script>
<el-pagination id="app"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="sizes, prev, pager, next, jumper, ->, total, slot"
:total="PageMAX">
el-pagination>
<script type="module">
new Vue({
el: '#app',
data() {
return {
searchKey: '',
tableData: [],
PageMAX: 0,
currentPage: 1,
pageSize: 10
};
},
mounted() {
this.getData();
},
methods: {
async getData() {
const response = await axios.get('/api/page', {
params: {
key: this.searchKey,
page: this.currentPage,
size: this.pageSize,
}
});
this.tableData = response.data.records;
this.PageMAX = response.data.pages;
},
handleSizeChange(val) {
this.pageSize = val;
this.getData();
},
handleCurrentChange(val) {
this.currentPage = val;
this.getData();
},
handleSearch() {
this.currentPage = 1;
this.getData();
}
}
})
script>
axios.post('/user',
{
"admin": this.admin
},
{
headers:{
"Content-Type": "application/json"
}
}
@ResponseBody
//执行更新
@RequestMapping(value = "/user",method = RequestMethod.POST)
public Object updateUser(@RequestBody Map<String,Object> params){
// System.out.println(admin);
// TAdmin admin = (TAdmin) params.get("admin");
String text = String.valueOf(params.get("admin"));
Gson gson = new Gson();
TAdmin admin = gson.fromJson(text, TAdmin.class);
boolean update = tAdminService.updateById(admin);
Map<String,Object> result = new HashMap<>();
result.put("state",update);
result.put("data",admin);
return result;
}
axios({
method: 'delete',
url: '/role',
data:{
"roles": this.multipleSelection
}
})
@ResponseBody
@DeleteMapping ("/role")
public Object deleteRole(@RequestBody Map<String,Object> roles) {
return "200";
}
以下面代码为例
model.addAttribute("access_token", 'access_token')`
前端可以绑定到span标签(input也是可以的,由于该数据不想显示出来所以直接隐藏掉就可以了),页面使用window.document.getElementById获取数据
<span style="display: none" th:text="${access_token}" id="access_token">span>
const access_token = window.document.getElementById('access_token').innerText
Vue:这样定义属性 一定要加上
④显示树状
//获取数据
function getTree() {
$.ajax({
url: "/menu/tree",
type: "get",
dataType: "json",
success: function (response) {
var result = response.result;
if (result === "SUCCESS") {
var zNodes = response.data;
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}
if (result === "FAILED") {
layer.msg(response.message);
}
}
})
}
docker 中安装MySQL :
docker pull mysql
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
在docker中安装了mysql后,用SQLyog去连接的时候出现如下错误。
解决方法:
1、查看我们想要连接的mysql是否启动
#查看在运行的容器
docker ps -s
2、进入容器
docker exec -it 容器号或名 /bin/bash
docker exec -it b30062adc08c /bin/bash
docker exec -it mysql /bin/bash
3、进入mysql
mysql -uroot -p
#输入密码(跟之前在windows上运行mysql是一样的)
4、查看MySQL的信息
select host,user,plugin,authentication_string from mysql.user;
备注:host为 % 表示不限制ip localhost表示本机使用 plugin非mysql_native_password 则需要修改密码
6、修改密码
mysql> use mysql;
mysql> alter user 'root'@'%' identified with mysql_native_password by '123456';
mysql> flush privileges;
mysql> select host,user,plugin,authentication_string from mysql.user;
再次用navicat远程连接mysql就成功了
发现读取不到Nacos配置中心的配置,后续发现控制台也不显示Nacos相关信息
从Spring Boot 2.4版本开始,配置文件加载方式进行了重构。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
dependency>
Spring Cloud 2020.0.0版本之前会自动引入Netflix Ribbon依赖,Netflix Ribbon功能跟loadbalancer一样,因Netflix公司停止维护Ribbon后, 在Spring Cloud 2020.0.0版本之后Spring使用loadbalancer替代了Ribbon, 但是loadbalancer依赖需要手动引入。
# 引入loadbalancer
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-loadbalancerartifactId>
dependency>
增加loadbalancer后启动提示:Spring Cloud LoadBalancer is currently working with the default cache. You can switch to using Caffeine cache, by adding it and org.springframework.cache.caffeine.CaffeineCacheManager to the classpath
解决方案是引入caffeine,或者关闭cache
<dependency>
<groupId>com.github.ben-manes.caffeinegroupId>
<artifactId>caffeineartifactId>
<version>3.1.0version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>5.2.21.RELEASEversion>
dependency>
tId>spring-cloud-loadbalancer
### 后续问题
增加loadbalancer后启动提示:Spring Cloud LoadBalancer is currently working with the default cache. You can switch to using Caffeine cache, by adding it and org.springframework.cache.caffeine.CaffeineCacheManager to the classpath
解决方案是引入caffeine,或者关闭cache
```xml
com.github.ben-manes.caffeine
caffeine
3.1.0
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>5.2.21.RELEASEversion>
dependency>
Failed to download repo vuejs-templates/webpacke: Response code 404 (Not Found)
执行下面三个命令来检查环境:
node -v(小写v) ;如果没有显示node版本,先去官网下载安装node
vue -V(大写V) ;如果没有显示vue版本,npm i vue-cli -g安装
webpack -v(小写v); 如果需要重新安装,就用 npm install webpack -g 和 npm i -g webpack-cli
跨域
问题描述:已拦截跨源请求:同源策略禁止读取位于 http://localhost:88/api/sys/login 的远程资源。(原因:CORS 头缺少 ‘Access-Control-Allow-Origin’)。
问题分析:这是一种跨域问题。访问的域名和端口和原来的请求不同,请求就会被限制
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js施加的安全限制。(ajax可以)
同源策略:是指协议,域名,端囗都要相同,其中有一个不同都会产生跨域;
跨域流程: 这个跨域请求的实现是通过预检请求实现的,先发送一个OPSTIONS探路,收到响应允许跨域后再发送真实请求
前面跨域的解决方案: 方法1:设置nginx包含admin和gateway 方法2:让服务器告诉预检请求能跨域
解决方法:在网关中定义“GulimallCorsConfiguration”类,该类用来做过滤,允许所有的请求跨域。
@Configuration // gateway
public class GulimallCorsConfiguration {
@Bean // 添加过滤器
public CorsWebFilter corsWebFilter(){
// 基于url跨域,选择reactive包下的
UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
// 跨域配置信息
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许跨域的头
corsConfiguration.addAllowedHeader("*");
// 允许跨域的请求方式
corsConfiguration.addAllowedMethod("*");
// 允许跨域的请求来源
corsConfiguration.addAllowedOrigin("*");
// 是否允许携带cookie跨域
corsConfiguration.setAllowCredentials(true);
// 任意url都要进行跨域配置
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
在springboot中配置跨域时,出现When allowCredentials is true, allowedOrigins cannot contain the special value “*” since that cannot be set on the “Access-Control-Allow-Origin” response header. To allow credentials to a set of origins, list them explicitly or consider using “allowedOriginPatterns” instead.
浏览器显示500服务器报错
需要将配置类中的corsConfiguration.addAllowedOrigin(“”);修改成为corsConfiguration.addAllowedOriginPattern(“”);
修改前的代码:
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 配置跨域
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许哪个请求头
corsConfiguration.addAllowedHeader("*");
// 允许哪个方法进行跨域
corsConfiguration.addAllowedMethod("*");
// 允许哪个请求来源进行跨域
corsConfiguration.addAllowedOrigin("*");
// 是否允许携带cookie进行跨域
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
修改后:
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 配置跨域
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许哪个请求头
corsConfiguration.addAllowedHeader("*");
// 允许哪个方法进行跨域
corsConfiguration.addAllowedMethod("*");
// 允许哪个请求来源进行跨域
// corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedOriginPattern("*");
// 是否允许携带cookie进行跨域
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
path中的p没有大写Path
- Path=/user/**,还有‘=’ 中间不能有空格
适用于vue-cli3以上版本搭建的项目
一、接口基址(单个接口地址)
如果你的项目只有一个服务器访问地址(接口地址),不调用别的接口地址的话可以直接在src下的main.js中设置axios的默认基址
//main.js中
import axios from 'axios'
Vue.prototype.$axios = axios
axios.defaults.baseURL='http://localhost:3000';//设置基址
例子如下
//example.vue
二、代理接口地址
如果你的项目需要访问多个地址,即调用多个不同的接口来进行数据的交互,这个时候应该设置代理地址
在项目根目录下新建一个vue.config.js文件
//vue.config.js
module.exports = {
devServer: {
host: 'localhost',
port: 8080,//本地运行的端口
open: true, //配置自动启动浏览器
hotOnly:false,
//接口代理
proxy: {
'/news': {
target: 'http://v.juhe.cn/toutiao',//设置要代理访问的接口---这是头条的接口
changeOrigin: true,
pathRewrite: {
'^/news': ''//重写访问地址,在请求时可以省略target的地址,直接以/news开头
}
},
'/api': {
target: 'http://localhost:3000',//设置要代理访问的接口----这是我自己的接口
changeOrigin: true,
pathRewrite: {
'^/api': '' //重写访问地址,在请求时可以省略target的地址,直接以/api开头
}
}
}
},
例子如下
//example.vue
原因是
public Result delete(@RequestBody Long[] ids){
//效验数据
AssertUtils.isArrayEmpty(ids, "id");
categoryService.deleteMenuById(Arrays.asList(ids));
return new Result();
}
用List[] 接收
之前axios 是 data:{} 带了key 所以接收不到
data: {
firstName: 'Fred'
},
// 发送请求体数据的可选语法
// 请求方式 post
// 只有 value 会被发送,key 则不会
data: 'Country=Brasil&City=Belo Horizonte',
在用到el-switch组件时,需要绑定数据为number类型,但总也绑不成功
element官方文档上说:设置该组件的active-value和inactive-value属性,可接受Boolean, String或Number类型的值。
找的解决办法:
1,使用number将绑定的v-model改为number类型
<el-switch
active-value=1
inactive-value=0
v-model.number=scope.row.locked>
el-switch>
这样没用,连switch按钮都绑定不上去
2、后来发现,当value为Number类型的时候active-value和inactive-value前边必须加:单项绑定一下才可以。
而active-value和inactive-value等号后边的值得引号是可有可无的。
<el-switch
:active-value=1
:inactive-value=0
v-model="scope.row.locked">
</el-switch>
Vue中的数据绑定
绑定数据有三种方式:
当加上v-bind:之后,它的值classe不是字符串,而是vue实例对应的data.classed的这个变量。也就是说data.classed是什么值,它就会给class属性传递什么值,当data.classed发生变化的时候,class属性也发生变化,这非常适合用在通过css来实现动画效果的场合。他只是单向变动
v-bind支持的类型
html中的属性、css的样式、对象、数组、number 类型、bool类型
v-bind使用:
// 绑定文本
// 绑定属性
// 绑定表达式
:class{className:true}
v-model
主要是用在表单元素中,它实现了双向绑定。在同事使用v-bind和v-model中,v-model建立的双向绑定对输入型元素input, textarea, select等具有优先权,会强制实行双向绑定。很多时候v-model使用在表单的中实现双向绑定。
<input v-model="something">
extension-configs:
- data-id: datasource.yaml
group: dev
refresh: true
请求域名可做拼接文件访问URL使用
然后下一步即可
上传文件时需要以上红框参数
<dependency>
<groupId>com.qcloudgroupId>
<artifactId>cos_apiartifactId>
<version>5.6.89version>
dependency>
tencent:
cos:
file:
keyId: keyId
keySecret: keySecret
bucketName: ed-1302656006
regionId: ap-guangzhou
cosHost: https://{请求域名} #例:https://ed-1302656006.cos.ap-guangzhou.myqcloud.com
@Component
@ConfigurationProperties(prefix = "tencent.cos.file")
@Data
public class CosConfig {
private String keyId;
private String keySecret;
private String bucketName;
private String regionId;
private String cosHost;
}
实际需分层(service/impl),这里仅为记录
@PutMapping()
public String uploadCosFile(@RequestPart("file") MultipartFile multipartFile) {
COSClient cosClient = initCosClient();
// spring直接使用File接收文件传参,会有问题(No primary or single unique constructor found for class java.io.File)不知道具体原因,之后再看。
// 腾讯云上传方法参数需要File,做一个转换操作
File file = MultipartFileToFile(multipartFile);
PutObjectRequest putObjectRequest = new PutObjectRequest(cosConfig.getBucketName(), key, file);
//对象键(Key)是对象在存储桶中的唯一标识。例如,在对象的访问域名 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/images/picture.jpg 中,对象键(key)为 images/picture.jpg
//如果images文件夹不存在则创建
cosClient.putObject(putObjectRequest);
client.shutdown(); // 关闭cos客户端
//ResponseParam为自定义返回json格式
return cosConfig.getCosHost() + "/" + key;
}
/**
* 获取腾讯云COS客户端
* @return COSClient
*/
private COSClient initCosClient() {
COSCredentials cred = new BasicCOSCredentials(cosConfig.getKeyId(), cosConfig.getKeySecret());
Region region = new Region(cosConfig.getRegionId());
ClientConfig clientConfig = new ClientConfig(region);
clientConfig.setHttpProtocol(HttpProtocol.https);
return new COSClient(cred, clientConfig);
}
/**
* 接口只能接受MultipartFile, 腾讯云需要File
* 故 MultipartFile => File
* @param multiFile 上传文件
* @return file
*/
public static File MultipartFileToFile(MultipartFile multiFile) {
// 获取文件名
String fileName = multiFile.getOriginalFilename();
// 获取文件后缀
String suffix = fileName.substring(fileName.lastIndexOf("."));
try {
// 防止生成的临时文件重复,文件名随机码, UUID
File file = File.createTempFile(UUID.randomUUID().toString().replaceAll("-", ""), suffix);
multiFile.transferTo(file);
return file;
} catch (Exception e) {
e.printStackTrace();
throw new 自定义异常(code, "MultipartFileToFile 文件转换异常");
}
}
转载 https://blog.csdn.net/Memory_y/article/details/127955778?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-127955778-blog-121015175.235%5Ev32%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-127955778-blog-121015175.235%5Ev32%5Epc_relevant_default_base3&utm_relevant_index=6
在“ gulimall-third-party”命名空间中,创建“ gulimall-third-party.yml”文件
spring:
cloud:
alicloud:
access-key:
secret-key:
oss:
endpoint:
bucker:
@Data
@Component
@ConfigurationProperties(prefix = "oss.yaml")
@RefreshScope
public class OssConfig {
@Value("${spring.cloud.alicloud.oss.endpoint}")
String endpoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
String bucket;
@Value("${spring.cloud.alicloud.access-key}")
String accessId;
@Value("${spring.cloud.alicloud.secret-key}")
String accessKey;
}
@Service
public class OssServiceImpl implements OssService {
@Resource
OssConfig ossConfig;
@Resource
OSSClient ossClient;
//获取签名
@Override
public Map<String, String> getPolicy() {
String host = "https://" + ossConfig.getBucket() + "." + ossConfig.getEndpoint(); // host的格式为 bucketname.endpoint
String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String dir = "product/"+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", ossConfig.getAccessId());
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
} finally {
ossClient.shutdown();
}
return respMap;
}
}
@Autowired
OssService ossService;
@GetMapping("/policy")
public Map<String,String> getPolicy(){
return ossService.getPolicy();
}
点击上传
只能上传jpg/png文件,且不超过10MB
前端的校验是element-ui表单验证
Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可。
@NotNull等
步骤1:使用校验注解
在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints”包中,提供了如@Email,@NotNull等注解。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
dependency>
里面依赖了hibernate-validator
在非空处理方式上提供了@NotNull,@NotBlank和@NotEmpty
The annotated element must not be null. Accepts any type.
注解元素禁止为null,能够接收任何类型
the annotated element must not be null nor empty.
该注解修饰的字段不能为null或""
Supported types are:
支持以下几种类型
CharSequence (length of character sequence is evaluated)字符序列(字符序列长度的计算)
Collection (collection size is evaluated)
集合长度的计算
Map (map size is evaluated)
map长度的计算
Array (array length is evaluated)
数组长度的计算
The annotated element must not be null and must contain at least one non-whitespace character. Accepts CharSequence.
该注解不能为null,并且至少包含一个非空格字符。接收字符序列。
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
测试: http://localhost:88/api/product/brand/save
validator”的“\org\hibernate\validator\ValidationMessages_zh_CN.properties”文件中。在该文件中定义了很多的错误规则:
javax.validation.constraints.AssertFalse.message = 只能为false
javax.validation.constraints.AssertTrue.message = 只能为true
想要自定义错误消息,可以覆盖默认的错误提示信息,如@NotBlank的默认message是
public @interface NotBlank {
String message() default "{javax.validation.constraints.NotBlank.message}";
}
可以在添加注解的时候,修改message:
@NotBlank(message = "品牌名必须非空")
private String name;
但是这种返回的错误结果并不符合我们的业务需要。
BindResult
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
if( result.hasErrors()){
Map<String,String> map=new HashMap<>(); //1.获取错误的校验结果
result.getFieldErrors().forEach((item)->{
//获取发生错误时的message
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();}
这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理。
统一异常处理@ControllerAdvice
可以使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。
@Slf4j
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
@ExceptionHandler(value = Exception.class) // 也可以返回ModelAndView
public R handleValidException(MethodArgumentNotValidException exception) {
Map<String, String> map = new HashMap<>();
// 获取数据校验的错误结果
BindingResult bindingResult = exception.getBindingResult();
bindingResult.getFieldErrors().forEach(fieldError -> {
String message = fieldError.getDefaultMessage();
String field = fieldError.getField();
map.put(field, message);
});
log.error("数据校验出现问题{},异常类型{}", exception.getMessage(), exception.getClass());
return R.error(400, "数据校验出现问题").put("data", map);
}
}
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
log.error("未知异常{},异常类型{}",throwable.getMessage(),throwable.getClass());
return R.error(400,"数据校验出现问题");
}
上面代码中,针对于错误状态码,是我们进行随意定义的,然而正规开发过程
中,错误状态码有着严格的定义规则,如该在项目中我们的错误状态码定义
为了定义这些错误状态码,我们可以单独定义一个常量类,用来存储这些错误状态码
package com.yxj.common.exception;
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*/
public enum BizCodeEnum {
UNKNOW_EXEPTION(10000,"系统未知异常"),
VALID_EXCEPTION( 10001,"参数格式校验失败");
private int code;
private String msg;
BizCodeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
场景:要校验showStatus的01状态,可以用正则,但我们可以利用其他方式解决
复杂场景。比如我们想要下面的场景
显示状态[0-不显示;1-显示]
@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
@ListValue(vals = {0,1}, groups = {AddGroup.class, UpdateGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
添加依赖
<dependency>
<groupId>javax.validationgroupId>
<artifactId>validation-apiartifactId>
<version>2.0.1.Finalversion>
dependency>
必须有3个属性
message()错误信息
groups()分组校验
payload()自定义负载信息
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
// 使用该属性去Validation.properties中取
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] value() default {};
}
该属性值取哪里取呢?
common创建文件ValidationMessages.properties
里面写上com.atguigu.common.valid.ListValue.message=必须提交指定的值 [0,1]
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set=new HashSet<>();
@Override
public void initialize(ListValue constraintAnnotation) {
int[] value = constraintAnnotation.value();
for (int i : value) {
set.add(i);
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
@Constraint(validatedBy = { ListValueConstraintValidator.class})
一个校验注解可以匹配多个校验器
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(value = {0,1},groups ={AddGroup.class})
private Integer showStatus;