完善品牌管理的添加功能-字段参数的前后端校验
完成阿里云OSS的上传功能之后,让它能显示出来:
来到品牌的组件src\views\modules\product\brand.vue,修改el-table-column标签:
<el-table-column prop="logo" header-align="center" align="center" label="品牌logo地址">
<template slot-scope="scope">
<img :src="scope.row.logo" style="width: 100px; height: 80px" />
</template>
</el-table-column>
之后到人人平台上传一个品牌即可看到图片:
添加前端校验功能
最后添加自定义的表单验证功能,官方给出的自定义校验方法关键字为validate。
brand-add-or-update.vue组件如下:
<template>
<el-dialog
:title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible"
>
<el-form
:model="dataForm"
:rules="dataRule"
ref="dataForm"
@keyup.enter.native="dataFormSubmit()"
label-width="140px"
>
<el-form-item label="品牌名" prop="name">
<el-input v-model="dataForm.name" placeholder="品牌名"></el-input>
</el-form-item>
<el-form-item label="品牌logo地址" prop="logo">
<!-- <el-input v-model="dataForm.logo" placeholder="品牌logo地址"></el-input> -->
<singleUpload v-model="dataForm.logo" ></singleUpload>
</el-form-item>
<el-form-item label="介绍" prop="descript">
<el-input v-model="dataForm.descript" placeholder="介绍"></el-input>
</el-form-item>
<el-form-item label="显示状态" prop="showStatus">
<el-switch
v-model="dataForm.showStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
></el-switch>
</el-form-item>
<el-form-item label="检索首字母" prop="firstLetter">
<el-input v-model="dataForm.firstLetter" placeholder="检索首字母"></el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model.number="dataForm.sort" placeholder="排序"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
import singleUpload from "@/components/upload/singleUpload"
export default {
components:{singleUpload},
data() {
return {
visible: false,
dataForm: {
brandId: 0,
name: "",
logo: "",
descript: "",
showStatus: 1,
firstLetter: "",
sort: ""
},
dataRule: {
name: [{ required: true, message: "品牌名不能为空", trigger: "blur" }],
logo: [
{ required: true, message: "品牌logo地址不能为空", trigger: "blur" }
],
descript: [
{ required: true, message: "介绍不能为空", trigger: "blur" }
],
showStatus: [{ required: true, message: "显示状态", trigger: "blur" }],
firstLetter: [
{ validator: (rule, value, callback) => {
if (value == "") {
callback(new Error("首字母必须填写"));
} else if (!/^[a-zA-Z]$/.test(value)) {
callback(new Error("首字母必须a-z或者A-Z之间"));
} else {
callback();
}
},
trigger: "blur" }
],
sort: [
{
validator: (rule, value, callback) => {
if (value == "") {
callback(new Error("排序字段必须填写"));
} else if (!Number.isInteger(value) || value<0) {
callback(new Error("排序必须是一个大于等于0的整数"));
} else {
callback();
}
},
trigger: "blur"
}
]
}
};
},
methods: {
init(id) {
this.dataForm.brandId = id || 0;
this.visible = true;
this.$nextTick(() => {
this.$refs["dataForm"].resetFields();
if (this.dataForm.brandId) {
this.$http({
url: this.$http.adornUrl(
`/product/brand/info/${this.dataForm.brandId}`
),
method: "get",
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 0) {
this.dataForm.name = data.brand.name;
this.dataForm.logo = data.brand.logo;
this.dataForm.descript = data.brand.descript;
this.dataForm.showStatus = data.brand.showStatus;
this.dataForm.firstLetter = data.brand.firstLetter;
this.dataForm.sort = data.brand.sort;
}
});
}
});
},
// 表单提交
dataFormSubmit() {
this.$refs["dataForm"].validate(valid => {
if (valid) {
this.$http({
url: this.$http.adornUrl(
`/product/brand/${!this.dataForm.brandId ? "save" : "update"}`
),
method: "post",
data: this.$http.adornData({
brandId: this.dataForm.brandId || undefined,
name: this.dataForm.name,
logo: this.dataForm.logo,
descript: this.dataForm.descript,
showStatus: this.dataForm.showStatus,
firstLetter: this.dataForm.firstLetter,
sort: this.dataForm.sort
})
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: "操作成功",
type: "success",
duration: 1500,
onClose: () => {
this.visible = false;
this.$emit("refreshDataList");
}
});
} else {
this.$message.error(data.msg);
}
});
}
});
}
}
};
</script>
添加后端校验功能(JSR303)
JSR303的规范中,有一系列固定的校验注解,具体源码在javax.validation.constraints包中。
首先给组件添加校验注解
在BrandEntity中,标注
package com.lastingwar.mall.product.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
/**
* 品牌
*
* @author yhm
* @email 403627000@qq.com
* @date 2020-05-29 16:37:08
*/
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名必须提交")
private String name;
/**
* 品牌logo地址
*/
@NotEmpty
@URL(message = "logo必须是一个合法的url地址")
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty
@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是一个字母")
private String firstLetter;
/**
* 排序
*/
@NotNull
@Min(value = 0,message = "排序必须大于等于0")
private Integer sort;
}
@NotBlank意思是至少有一个非空字符。@URL表示内容必须是一个URL地址,@Pattern指信息必须满足自定义的正则表达式,@Min表示值必须大于等于定义的value。
之后在响应控制中添加注解
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
不在响应中添加注解默认不启用后台检测。
之后可以在postman中进行检测,发送:
{"name":"aaa",
"logo":"adad"}
响应内容如下:
{
"timestamp": "2020-06-04T05:28:32.543+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotNull.brandEntity.sort",
"NotNull.sort",
"NotNull.java.lang.Integer",
"NotNull"
],
"arguments": [
{
"codes": [
"brandEntity.sort",
"sort"
],
"arguments": null,
"defaultMessage": "sort",
"code": "sort"
}
],
"defaultMessage": "不能为null",
"objectName": "brandEntity",
"field": "sort",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotNull"
},
{
"codes": [
"URL.brandEntity.logo",
"URL.logo",
"URL.java.lang.String",
"URL"
],
"arguments": [
{
"codes": [
"brandEntity.logo",
"logo"
],
"arguments": null,
"defaultMessage": "logo",
"code": "logo"
},
[],
{
"arguments": null,
"defaultMessage": "",
"codes": [
""
]
},
-1,
{
"arguments": null,
"defaultMessage": "",
"codes": [
""
]
},
{
"arguments": null,
"defaultMessage": ".*",
"codes": [
".*"
]
}
],
"defaultMessage": "logo必须是一个合法的url地址",
"objectName": "brandEntity",
"field": "logo",
"rejectedValue": "adad",
"bindingFailure": false,
"code": "URL"
},
{
"codes": [
"NotEmpty.brandEntity.firstLetter",
"NotEmpty.firstLetter",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"brandEntity.firstLetter",
"firstLetter"
],
"arguments": null,
"defaultMessage": "firstLetter",
"code": "firstLetter"
}
],
"defaultMessage": "不能为空",
"objectName": "brandEntity",
"field": "firstLetter",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotEmpty"
}
],
"message": "Validation failed for object='brandEntity'. Error count: 3",
"path": "/product/brand/save"
}
做一个统一的异常处理功能
创建一个文件来接受数据校验的异常,并抛出:mall-product/src/main/java/com/lastingwar/mall/product/exception/ExceptionControllerAdvice.java
内容如下:
package com.lastingwar.mall.product.exception;
import com.lastingwar.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* 集中处理所有异常
*/
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.lastingwar.mall.product.controller")
@RestControllerAdvice(basePackages = "com.lastingwar.mall.product.controller")
public class ExceptionControllerAdvice {
//介绍异常的类型
@ExceptionHandler(value= MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException e){
//错误日志打印的内容
log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
//接受返回的异常结果
BindingResult bindingResult = e.getBindingResult();
//存放异常的map
Map<String,String> errorMap = new HashMap<>();
//数据监测内容比较多,遍历成映射添加进map
bindingResult.getFieldErrors().forEach((fieldError)->{
errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
});
//返回的json内容
return R.error(400,"数据校验出现问题").put("data",errorMap);
}
//接受其他所有错误
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}
编写完成之后重启服务,来到postman测试,返回编写的内容
注意由于微服务众多,之后会有大量的异常,异常状态码是定位异常的主要依据,所以对异常状态码要有规范性的标准。
所以在common模块中添加一个枚举类来保存状态码:
mall-common/src/main/java/com/lastingwar/common/exception/BizCodeEnume.java
内容:
package com.lastingwar.common.exception;
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*
*
*/
public enum BizCodeEnume {
UNKNOW_EXCEPTION(10000,"系统未知异常"),
VALID_EXCEPTION(10001,"参数格式校验失败");
private int code;
private String msg;
BizCodeEnume(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
之后就能够在控制响应那边调用枚举类:
package com.lastingwar.mall.product.exception;
import com.lastingwar.common.exception.BizCodeEnume;
import com.lastingwar.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* 集中处理所有异常
*/
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.lastingwar.mall.product.controller")
@RestControllerAdvice(basePackages = "com.lastingwar.mall.product.controller")
public class ExceptionControllerAdvice {
//介绍异常的类型
@ExceptionHandler(value= MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException e){
//错误日志打印的内容
log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
//接受返回的异常结果
BindingResult bindingResult = e.getBindingResult();
//存放异常的map
Map<String,String> errorMap = new HashMap<>();
//数据监测内容比较多,遍历成映射添加进map
bindingResult.getFieldErrors().forEach((fieldError)->{
errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
});
//返回的json内容
return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(),BizCodeEnume.VALID_EXCEPTION.getMsg()).put("data",errorMap);
}
//接受其他所有错误
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}
再用postman进行测试即可看到枚举类的信息