栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

谷粒商城--品牌管理开发-笔记六

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

谷粒商城--品牌管理开发-笔记六

谷粒商城–品牌管理开发-笔记六 1.新增品牌管理菜单

2.前端页面

由于只有简单的crud使用逆向工程生成的vue文件

将gulimall-product模块生成的逆向工程文件中的category.vue和category-add-or-update.vue复制到

renren-fast-vue/src/views/modules/product目录下

复制完成后 页面刷新结果

此时已经自带crud

3.样式优化

将显示状态字段修改为开关显示

src/views/modules/product/brand.vue将template中的显示状态字段修改为以下代码

	
        
      

新增updateBrandStatus方法

	//修改显示状态方法
	updateBrandStatus(data) {
      console.log("最新信息", data);
      let { brandId, showStatus } = data;
      //发送请求修改状态
      this.$http({
        url: this.$http.adornUrl("/product/brand/update/status"),
        method: "post",
        data: this.$http.adornData({ brandId, showStatus }, false)
      }).then(({ data }) => {
        this.$message({
          type: "success",
          message: "状态更新成功"
        });
      });
    },

后台编写接口 并重启

gulimall-product/src/main/java/site/zhourui/gulimall/product/controller/BrandController.java

	
    @RequestMapping("/update/status")
    //@RequiresPermissions("product:brand:update")
    public R updateStatus(@RequestBody BrandEntity brand){
        brandService.updateById(brand);

        return R.ok();
    }

网关配置商品相关路由 并重启网关

gulimall-gateway/src/main/resources/application.yml

- id: product_route
  uri: lb://gulimall-product
  predicates:
    - Path=/api/product/**
  filters:
    - RewritePath=/api/(?.*),/${segment}

修改模态框中的显示状态字段

src/views/modules/product/brand-add-or-update.vue

	
        
      

效果:

4.云存储管理 0.为什么使用存储
  1. 分布式项目上传图片,不可能是只在某一台服务器上,但如果所有服务器上都有同样的资源,又造成资源浪费
  2. 解决方法
  • 自建文件服务器 :搭建复杂,维护成本高,前期费用高
  • 云存储:即开即用,无需维护,按量收费

为了品牌logo上传图片,使用阿里云云存储管理图片

1.登录注册阿里云

本人已经注册过阿里云账号了,没有的请自行注册

2.登录成功后

进入控制台

3.选择对象存储oss

4.开通对象存储服务 OSS

5.使用OSS 1.创建Bucket

2.上传测试

阿里云文件上传文档

1.添加依赖包

gulimall-product/pom.xml


    com.aliyun.oss
    aliyun-sdk-oss
    3.8.0

2.添加测试方法

gulimall-product/src/test/java/site/zhourui/gulimall/product/GulimallProductApplicationTests.java

		// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
        String endpoint = "yourEndpoint";
        // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
        String accessKeyId = "yourAccessKeyId";
        String accessKeySecret = "yourAccessKeySecret";
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "examplebucket";
        // 填写文件名。文件名包含路径,不包含Bucket名称。例如exampledir/exampleobject.txt。
        String objectName = "exampledir/exampleobject.txt";

        OSS ossClient = null;
        try {
            // 创建OSSClient实例。
            ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

            String content = "Hello OSS";
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));
        } catch (OSSException e) {
            e.printStackTrace();
        } finally {
            // 关闭OSSClient。
            ossClient.shutdown();
        }
3.endpoint与Bucket 域名

4.配置API访问子账号

阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。

5.创建RAM子账号

6.为子账号添加oss权限

7.子账号创建AccessKey

8.配置完成过后进行测试
package site.zhourui.gulimall.product;


import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.ByteArrayInputStream;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallProductApplicationTests {

    @Test
   public void contextLoads() {
        // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
        String endpoint = "oss-cn-chengdu.aliyuncs.com";
        // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
        String accessKeyId = "xxxxxxxxxx";
        String accessKeySecret = "xxxxxxx";
        // 填写Bucket名称,例如examplebucket。
        String bucketName = "zr-gulimall-images";
        // 填写文件名。文件名包含路径,不包含Bucket名称。例如exampledir/exampleobject.txt。
        String objectName = "C:\Users\eric\Pictures\微信截图_20210930112358.png";

        OSS ossClient = null;
        try {
            // 创建OSSClient实例。
            ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

            String content = "Hello OSS";
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content.getBytes()));
        } catch (OSSException e) {
            e.printStackTrace();
        } finally {
            // 关闭OSSClient。
            ossClient.shutdown();
        }
    }

}

结果:成功

3.更为简单的使用方式,是使用SpringCloud Alibaba-Oss

相关文档

1.创建gulimall-third-party模块

将所有三方服务抽取到一个单独的模块

pom文件



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.1.RELEASE
         
    
    com.atguigu.gulimall
    gulimall-third-party
    0.0.1-SNAPSHOT
    gulimall-third-party
    第三方服务

    
        1.8
        Hoxton.RC1
    

    
        
            com.zhourui.gulimall
            gulimall-common
            0.0.1-SNAPSHOT
            
                
                    com.baomidou
                    mybatis-plus-boot-starter
                
            
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alicloud-oss
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
                
                    com.alibaba.cloud
                    spring-cloud-alibaba-dependencies
                    2.1.0.RELEASE
                    pom
                    import
                
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

    
        
            spring-milestones
            Spring Milestones
            https://repo.spring.io/milestone
        
    



GulimallThirdPartyApplication

@EnableDiscoveryClient
@SpringBootApplication
public class GulimallThirdPartyApplication {

    public static void main(String[] args) {
        SpringApplication.run(GulimallThirdPartyApplication.class, args);
    }

}

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      access-key: xxx
      secret-key: xxx
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
        bucket: gulimall-hello

  application:
    name: gulimall-third-party

server:
  port: 30000

2.上传测试
@SpringBootTest
class GulimallThirdPartyApplicationTests {

    @Autowired
    OSSClient ossClient;

    @Test
    public void testUpload() throws FileNotFoundException {
        // Endpoint以杭州为例,其它Region请按实际情况填写。
        String endpoint = "oss-cn-chengdu.aliyuncs.com";
        // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
        String accessKeyId = "xxx";
        String accessKeySecret = "xxx";

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 上传文件流。
        InputStream inputStream = new FileInputStream("C:\Users\eric\Pictures\sku_state_change_process.bpmn20.png");

        ossClient.putObject("zr-gulimall-images", "hahaha.jpg", inputStream);

        // 关闭OSSClient。
        ossClient.shutdown();

        System.out.println("上传完成...");
    }
}

测试结果

3.编写OssController接口

gulimall-third-party/src/main/java/site/zhourui/gulimall/thirdparty/controller/OssController.java

package site.zhourui.gulimall.thirdparty.controller;

import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import site.zhourui.common.utils.R;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.linkedHashMap;
import java.util.Map;

@RestController
public class OssController {

    @Autowired
    OSS ossClient;

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;
    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;

    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;


    @RequestMapping("/oss/policy")
    public R policy() {
        //https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg

        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
//        String callbackUrl = "http://88.88.88.88:8888";
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format + "/"; // 用户上传文件时指定的前缀。

        Map 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();
            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);
    }
}

接口测试

localhost:30000/oss/policy

测试结果

{
    "msg": "success",
    "code": 0,
    "data": {
        "accessid": "LTAI5t83gcohLLuvbzxxxxx",
        "policy": "eyJleHBpcmF0aW9uIjoiMjAyMS0xMC0wOFQxMDowNDoyNy4xNTZaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF0sWyJzdGFydHMtd2l0aCIsIiRrZXkiLCIyMDIxLTEwLTA4LyJdXX0=",
        "signature": "eFxjAhr4uWJxR1IkJftKpUWFzGU=",
        "dir": "2021-10-08/",
        "host": "https://zr-gulimall-images.oss-cn-chengdu.aliyuncs.com",
        "expire": "1633687467"
    }
}

5.阿里云对象存储上传方式 1.普通上传

优点: 安全性高

缺点:每次上传都通过服务器上传,造成服务器性能损失

2.服务器签名后前端直传

由服务器端获取认证,返回给前端,这样安全性的问题也解决了,服务器性能上的问题也解决了

因此本次我们选用第二种

5.品牌logo上传 1.编写上传组件

目录结构

multiUpload.vue

并配置el-uploadaction属性 格式:http://bucket.endpoint





singleUpload.vue

单文件上传组件

并配置el-uploadaction属性 格式:http://bucket.endpoint






policy.js

用于发送请求调用gulimall-third-party获取oss的认证信息

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);
        })
    });
}
2.配置三方接口的模块相关路由

gulimall-gateway/src/main/resources/application.yml增加以下配置,并重启

		- id: third_party_route
          uri: lb://gulimall-third-party
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/thirdparty/(?.*),/${segment}
3.在brand-add-or-update.vue中使用上传组件

此处我们需要上传的logo,所以选用单文件上传组件singleUpload.vue

1.引入组件
import SingleUpload from "@/components/upload/singleUpload";
2.注册组件
components: { SingleUpload },
3.使用组件

  

4.OSS跨域问题

问题

到这一步文件上传会出现跨域问题

解决

阿里云配置跨域设置

找到相应bucket的权限管理

找到跨域配置点击设置

新建规则,并配置

来源:允许谁跨域访问,可以设置自己的域名,这里我设置的*,就是所有

methods:允许跨域请求方式,文件上传使用的是post请求,这里选择post,如果对oss有其他操作,可以勾选其他方法

允许header:请求需要带什么头部信息,可以自定义头信息,做进一步安全验证,这里我选的是任意*

结果

上传成功返回图片链接并回显

6.品牌管理logo显示
	
        
      

效果:

7.关于新增按钮与批量按钮不显示问题

brand.vue中的isAuth为true才会显示

而这需要权限,目前我们没有,所以修改这个方法一直返回true即可

isAuth在src/utils/index.js

8.表单前端校验

参考文档

品牌新增与品牌修改时防止用于向服务端提交错误的数据

完整示例





效果

9.JSR303校验

只有前端校验是不够的,为了防止绕过前端验证的非法提交,在后端也需要表单提交的数据进行校验

此处使用的是JSR303校验

比如使用postman直接访问http://localhost:88/api/product/brand/save

这样就绕过了前端验证

1.步骤1:使用校验注解

在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints”包中,提供了如@Email,@NotNull等注解。

在非空处理方式上提供了@NotNull,@Blank和@NotBlank

  • @NotEmpty

    • The annotated element must not be {@code null} nor empty. Supported types are:

    • 不能是null

    • 不能是空字符

    • 集合框架中的元素不能为空

  • @NotNul

    • 被修饰元素不能为null
  • @NotBlank

    • The annotated element must not be {@code null} and must contain at least onenon-whitespace character.
    • 这个注解用来判断字符串或者字符

gulimall-product/src/main/java/site/zhourui/gulimall/product/entity/BrandEntity.java

为name字段添加添加注解

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	
	@TableId
	private Long brandId;
	
	@NotBlank
	private String name;
	
	private String logo;
	
	private String descript;
	
	@TableLogic(value = "1",delval = "0")
	private Integer showStatus;
	
	private String firstLetter;
	
	private Integer sort;

}
2.步骤2:使用校验注解@Valid,开启校验

在需要表单验证的controller接口上添加注解 并重启

在品牌的save方法的参数上添加注解@Valid

 	
    @RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand){
		brandService.save(brand);
        return R.ok();
    }
3.测试

postman直接访问http://localhost:88/api/product/brand/save

表单提交失败了

能够看到"defaultMessage": “不能为空”,这些错误消息定义在“hibernate-validator”的“orghibernatevalidatorValidationMessages_zh_CN.properties”文件中。在该文件中定义了很多的错误规则:

orghibernatevalidatorhibernate-validator6.0.17.Finalhibernate-validator-6.0.17.Final.jar!orghibernatevalidatorValidationMessages_zh_CN.properties

javax.validation.constraints.AssertFalse.message     = 只能为false
javax.validation.constraints.AssertTrue.message      = 只能为true
javax.validation.constraints.DecimalMax.message      = 必须小于或等于{value}
javax.validation.constraints.DecimalMin.message      = 必须大于或等于{value}
javax.validation.constraints.Digits.message          = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
javax.validation.constraints.Email.message           = 不是一个合法的电子邮件地址
javax.validation.constraints.Future.message          = 需要是一个将来的时间
javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
javax.validation.constraints.Max.message             = 最大不能超过{value}
javax.validation.constraints.Min.message             = 最小不能小于{value}
javax.validation.constraints.Negative.message        = 必须是负数
javax.validation.constraints.NegativeOrZero.message  = 必须是负数或零
javax.validation.constraints.NotBlank.message        = 不能为空
javax.validation.constraints.NotEmpty.message        = 不能为空
javax.validation.constraints.NotNull.message         = 不能为null
javax.validation.constraints.Null.message            = 必须为null
javax.validation.constraints.Past.message            = 需要是一个过去的时间
javax.validation.constraints.PastOrPresent.message   = 需要是一个过去或现在的时间
javax.validation.constraints.Pattern.message         = 需要匹配正则表达式"{regexp}"
javax.validation.constraints.Positive.message        = 必须是正数
javax.validation.constraints.PositiveOrZero.message  = 必须是正数或零
javax.validation.constraints.Size.message            = 个数必须在{min}和{max}之间

org.hibernate.validator.constraints.CreditCardNumber.message        = 不合法的信用卡号码
org.hibernate.validator.constraints.Currency.message                = 不合法的货币 (必须是{value}其中之一)
org.hibernate.validator.constraints.EAN.message                     = 不合法的{type}条形码
org.hibernate.validator.constraints.Email.message                   = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message                  = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.CodePointLength.message         = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.LuhnCheck.message               = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message              = ${validatedValue}的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message              = ${validatedValue}的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message                = ${validatedValue}的校验码不合法, ${modType}校验和不匹配
org.hibernate.validator.constraints.NotBlank.message                = 不能为空
org.hibernate.validator.constraints.NotEmpty.message                = 不能为空
org.hibernate.validator.constraints.ParametersscriptAssert.message  = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.Range.message                   = 需要在{min}和{max}之间
org.hibernate.validator.constraints.SafeHtml.message                = 可能有不安全的HTML内容
org.hibernate.validator.constraints.scriptAssert.message            = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.URL.message                     = 需要是一个合法的URL

org.hibernate.validator.constraints.time.DurationMax.message        = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
org.hibernate.validator.constraints.time.DurationMin.message        = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}

想要自定义错误消息,可以覆盖默认的错误提示信息,如@NotBlank的默认message是可以在添加注解的时候,修改message:

	
	@NotBlank(message = "品牌名称不能为空")
	private String name;

再次访问

但是这种返回的错误结果并不符合我们的业务需要。

4.步骤3:自定义的封装校验结果 1.获取校验结果

给校验的Bean后,紧跟一个BindResult,就可以获取到校验的结果。拿到校验的结果,就可以自定义的封装。

@RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
        if( result.hasErrors()){
            Map 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();
    }

测试http://localhost:88/api/product/brand/save

这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理。

5.步骤4:统一异常处理 1.创建异常处理类

可以使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。

gulimall-product/src/main/java/site/zhourui/gulimall/product/exception/GulimallExceptionControllerAdvice.java下创建

先删除以前单个处理

@RequestMapping("/save")
    public R save(@Valid @RequestBody BrandEntity brand){
//        if( result.hasErrors()){
//            Map 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);

抽取一个异常处理类

gulimall-product/src/main/java/site/zhourui/gulimall/product/exception/GulimallExceptionControllerAdvice.java

@Slf4j
@ResponseBody
@ControllerAdvice(basePackages = "site.zhourui.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {


    @ExceptionHandler(value = Exception.class)
    public R handlevalidException(MethodArgumentNotValidException exception){
        Map 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);
    }
}

测试结果http://localhost:88/api/product/brand/save

2.错误状态码

上面代码中,针对于错误状态码,是我们进行随意定义的,然而正规开发过程中,错误状态码有着严格的定义规则,如该在项目中我们的错误状态码定义

为了定义这些错误状态码,我们可以单独定义一个常量类,用来存储这些错误状态码

gulimall-common/src/main/java/site/zhourui/common/exception/BizCodeEnume.java

在gulimall-common公共模块

如果之后有需要增加错误信息,就在下面添加

package site.zhourui.common.exception;


public enum BizCodeEnume {
    UNKNOW_EXCEPTION(10000,"系统未知异常"),
    VAILD_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;
    }
}
3.修改统一异常处理类

使用BizCodeEnume枚举替换GulimallExceptionControllerAdvice中的code与msg

新增其他所有异常处理

可以使用

@RestControllerAdvice(basePackages = "site.zhourui.gulimall.product.controller")

替换

@ResponseBody
@ControllerAdvice(basePackages = "site.zhourui.gulimall.product.controller")
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "site.zhourui.gulimall.product.controller")
@RestControllerAdvice(basePackages = "site.zhourui.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {


    //处理参数验证异常
    @ExceptionHandler(value= MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();

        Map errorMap = new HashMap<>();
        //遍历bindingResult所有错误,获取错误字段,以及对应的消息,存储为map返回
        bindingResult.getFieldErrors().forEach((fieldError)->{
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
    }
    //其他所有异常
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){

        log.error("错误:",throwable);
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }
}
10.JSR303分组校验(完成多场景的复杂校验)

比如此处对品牌的新增与修改都是接收的BrandEntity而BrandEntity每个字段上的校验注解现在只针对save也就是新增方法,而修改所需要校验的字段又有所不同这时我们需要分组校验

1.新增接口AddGroup,UpdateGroup

这两个主要用来区分是属于新增分组或者修改分组,主要是用做标识符

gulimall-common/src/main/java/site/zhourui/common/valid/AddGroup.java

package site.zhourui.common.valid;

public interface AddGroup {
}

gulimall-common/src/main/java/site/zhourui/common/valid/UpdateGroup.java

package site.zhourui.common.valid;

public interface UpdateGroup {
}
2.新增接口UpdateStatusGroup

gulimall-common/src/main/java/site/zhourui/common/valid/UpdateStatusGroup.java

package site.zhourui.common.valid;

public interface UpdateStatusGroup {
}
3.给校验注解,标注上groups,指定什么情况下才需要进行校验
package site.zhourui.gulimall.product.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import site.zhourui.common.valid.AddGroup;
import site.zhourui.common.valid.ListValue;
import site.zhourui.common.valid.UpdateGroup;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;


@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	
	@NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class})
	@Null(message = "新增不能指定id",groups = {AddGroup.class})
	@TableId
	private Long brandId;
	
	@NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
	private String name;
	
	private String logo;
	
	private String descript;
	
	private Integer showStatus;
	
	private String firstLetter;
	
	private Integer sort;

}

对品牌id与品牌名进行分组校验

  • 品牌id
    • 新增AddGroup分组:@Null(message = “新增不能指定id”,groups = {AddGroup.class}) , 新增分组的请求brandId为空才可以
    • 修改UpdateGroup分组:@NotNull(message = “修改必须指定品牌id”,groups = {UpdateGroup.class}),修改分组的请求brandId不能为空
  • 品牌名
    • @NotBlank(message = “品牌名必须提交”,groups = {AddGroup.class,UpdateGroup.class}) ,修改分组与新增分组都必须不为空
4业务方法参数上使用@Validated注解

gulimall-product/src/main/java/site/zhourui/gulimall/product/controller/BrandController.java

save方法修改@Valid为分组校验注解@Validated({AddGroup.class})

updateStatus方法添加@Validated(UpdateStatusGroup.class)

package site.zhourui.gulimall.product.controller;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import site.zhourui.common.valid.AddGroup;
import site.zhourui.common.valid.UpdateGroup;
import site.zhourui.common.valid.UpdateStatusGroup;
import site.zhourui.gulimall.product.entity.BrandEntity;
import site.zhourui.gulimall.product.service.BrandService;
import site.zhourui.common.utils.PageUtils;
import site.zhourui.common.utils.R;

import javax.validation.Valid;



@RestController
@RequestMapping("product/brand")
public class BrandController {
    @Autowired
    private BrandService brandService;

    
    @RequestMapping("/list")
    public R list(@RequestParam Map params){
        PageUtils page = brandService.queryPage(params);

        return R.ok().put("page", page);
    }


    
    @RequestMapping("/info/{brandId}")
    public R info(@PathVariable("brandId") Long brandId){
		BrandEntity brand = brandService.getById(brandId);

        return R.ok().put("brand", brand);
    }

    @RequestMapping("/save")
    public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
//        if( result.hasErrors()){
//            Map 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();
    }

    
    @RequestMapping("/update/status")
    //@RequiresPermissions("product:brand:update")
    public R updateStatus(@RequestBody BrandEntity brand){
        brandService.updateById(brand);

        return R.ok();
    }

    
    @RequestMapping("/update")
    public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){
		brandService.updateById(brand);

        return R.ok();
    }

    
    @RequestMapping("/delete")
    public R delete(@RequestBody Long[] brandIds){
		brandService.removeByIds(Arrays.asList(brandIds));

        return R.ok();
    }

}

测试结果一:新增接口带上brandId,并且name为""

http://localhost:88/api/product/brand/save

测试结果二:修改接口不带上brandId,并且name为""

http://localhost:88/api/product/brand/update

因为showStatus字段的校验的特殊,所以我们需要自定义校验注解

5.自定义校验注解 1.编写一个自定义的校验注解@ListValue
package site.zhourui.common.valid;

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 = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{site.zhourui.common.valid.ListValue.message}";

    Class[] groups() default { };

    Class[] payload() default { };

    int[] vals() default { };
}

2.编写使用消息返回ValidationMessages.properties

site.zhourui.common.valid.ListValue.message=必须提交指定的值
3.编写一个自定义的校验器ListValueConstraintValidator
package site.zhourui.common.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class ListValueConstraintValidator implements ConstraintValidator {

    private Set set = new HashSet<>();
    //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {

        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
            set.add(val);
        }

    }

    //判断是否校验成功

    
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

        return set.contains(value);
    }
}

4.关联自定义的校验器和自定义的校验注解

修改@ListValue

package site.zhourui.common.valid;

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.atguigu.common.valid.ListValue.message}";

    Class[] groups() default { };

    Class[] payload() default { };

    int[] vals() default { };
}

5.使用实例

在BrandEntity使用自定义注解

	
	@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
	@ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})
	@TableLogic(value = "1",delval = "0")
	private Integer showStatus;

gulimall-product/src/main/java/site/zhourui/gulimall/product/controller/BrandController.java

updateStatus方法添加@Validated(UpdateStatusGroup.class)

	
    @RequestMapping("/update/status")
    //@RequiresPermissions("product:brand:update")
    public R updateStatus(@Validated(UpdateStatusGroup.class) @RequestBody BrandEntity brand){
        brandService.updateById(brand);

        return R.ok();
    }

测试http://localhost:88/api/product/brand/update/status

BrandEntity 后端校验全部配置

package site.zhourui.gulimall.product.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import site.zhourui.common.valid.AddGroup;
import site.zhourui.common.valid.ListValue;
import site.zhourui.common.valid.UpdateGroup;
import site.zhourui.common.valid.UpdateStatusGroup;

import javax.validation.constraints.*;


@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	
	@NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class})
	@Null(message = "新增不能指定id",groups = {AddGroup.class})
	@TableId
	private Long brandId;
	
	@NotBlank(message = "品牌名必须非空",groups = {UpdateGroup.class,AddGroup.class})
	private String name;
	
	//品牌logo地址不能为空,并且logo必须是一个合法的url地址
	@NotBlank(groups = {AddGroup.class})
	@URL(message = "logo必须是一个合法的url地址",groups={AddGroup.class,UpdateGroup.class})
	private String logo;
	
	//介绍不做限制
	private String descript;
	
    //自定义校验注解
	@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
	@ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})
	private Integer showStatus;
	
	@NotEmpty(groups={AddGroup.class})
	@Pattern(regexp="^[a-zA-Z]$",message = "检索首字母必须是一个字母",groups={AddGroup.class,UpdateGroup.class})
	private String firstLetter;
	
	@NotNull(groups={AddGroup.class})
	@Min(value = 0,message = "排序必须大于等于0",groups={AddGroup.class,UpdateGroup.class})
	private Integer sort;

}

测试效果http://localhost:88/api/product/brand/save

注意

默认情况下,在分组校验情况下,没有指定指定分组的校验注解,将不会生效,它只会在不分组的情况下生效。

	
	//介绍不做限制
	private String descript;
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/305852.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号