分布式电商项目二十七:完善品牌管理的添加功能-字段参数的前后端校验

完善品牌管理的添加功能-字段参数的前后端校验

完成阿里云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进行测试即可看到枚举类的信息
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值