up:: SpringBoot电商项目商品分类模块介绍

说明:

(1) 本篇博客需要注意的点:

● 对于Spring Boot来说,POST请求时,更建议使用【@RequestBody 实体类】的方式,去接收参数;(这一点还有不清晰的地方,需要回来修正); _ (是不是如果请求参数很多的时候,参数就尽量不要放在url后面,否则的话会很长;更好的做法就是,把参数通过JSON的方式,放在body中;;;那么,与此对应,Controller就需要使用@RequestBody来接收参数)_

● 【承接数据表的实体类】和【接收参数的实体类】,要分别创建,不要混用;

● Controller拿到参数后,需要首先校验下参数是否为空;本篇博客中,还是采用笨笨的方式去校验;后面会介绍【@Valid注解】;

● 【增加目录分类】接口要求:【用户必须登录,且该用户需要是管理员用户】;我们需要编写对应的逻辑;

● 我们的自定义异常ImoocMallException,在逻辑上其实是运行时异常;那么为了更方便的处理这个异常,可以让ImoocMallException继承RuntimeException;关于这个内容,在【附加自定义异常继承Exception和RuntimeException的区别】做了详细介绍;

● 这儿介绍了一个中快速拷贝复制对象属性的工具:【BeanUtils.copyProperties(obj1, obj2)】;

●【增加目录分类】接口要求:不允许分类名重复;

● 针对【POST请求,有的参数放在了url后面,有的放在了body中】;针对这个迷惑点,自己在【附加POST请求方法参数放在url中和放在body中有什么区别】中,进行了详细介绍;同时在这篇博客中,也详细总结了@Param,@RequestParam,@RequestBody这三个【与接收参数有关】的注解;


一:【增加目录分类】接口:说明;

这儿需要说明:请求中的body,是什么意思;和前面的没有body的区别;


二:正式开发;

1.创建CategoryController,并创建【增加目录分类】的方法:addCategory()方法;

CategoryController类:

 
     package com.imooc.mall.controller;
 
     import com.imooc.mall.common.ApiRestResponse;
     import com.imooc.mall.common.Constant;
     import com.imooc.mall.exception.ImoocMallExceptionEnum;
     import com.imooc.mall.model.pojo.User;
     import com.imooc.mall.model.request.AddCategoryReq;
     import com.imooc.mall.service.CategoryService;
     import com.imooc.mall.service.UserService;
     import org.springframework.beans.factory.annotation.Autowired;
     import org.springframework.stereotype.Controller;
     import org.springframework.web.bind.annotation.PostMapping;
     import org.springframework.web.bind.annotation.RequestBody;
     import org.springframework.web.bind.annotation.ResponseBody;
 
     import javax.servlet.http.HttpSession;
 
     /**
      * 描述:商品分类Controller
      */
     @Controller
     public class CategoryController {
         @Autowired
         UserService userService;
         @Autowired
         CategoryService categoryService;
 
         /**
          * 增加目录分类
          *
          * @param session
          * @param addCategoryReq
          * @return
          */
         @PostMapping("/admin/category/add")
         @ResponseBody
         public ApiRestResponse addCategory(HttpSession session, @RequestBody AddCategoryReq addCategoryReq) {
             //校验入参,如果为空就返回【参数不能为空】的信息
             if (addCategoryReq.getName() == null ||
                     addCategoryReq.getOrderNum() == null ||
                     addCategoryReq.getParentId() == null ||
                     addCategoryReq.getType() == null) {
                 return ApiRestResponse.error(ImoocMallExceptionEnum.NEED_USER_NAME.NAME_NOT_NULL);
             }
 
 
             //尝试获取当前登录用户
             User currentUser = (User)
							session.getAttribute(Constant.IMOOC_MALL_USER);
             //如果获取不到,说明当前没有用户登录;就返回【用户未登录】的信息
             if (currentUser == null) {
                 return ApiRestResponse.error(ImoocMallExceptionEnum.NEED_LOGIN);
             }
             //校验当前用户是否是管理员用户
             boolean isAdminRole = userService.checkAdminRole(currentUser);
 
 
             if (isAdminRole) {//如果是管理用户;就去执行【增加目录分类】;
                 categoryService.add(addCategoryReq);
                 return ApiRestResponse.success();
             } else {//如果不是管理员用户;就返回【无管理员权限】的信息
                 return ApiRestResponse.error(ImoocMallExceptionEnum.NEED_ADMIN);
             }
         }
     }
 
 

说明:

(1) 对于Spring Boot来说,POST请求时,更建议使用【@RequestBody 实体类】的方式,去接收参数;

先看下这个接口的参数 :

如果,我们采用前面的【@RequestParam("") 参数】的方式去接收参数,那么我们就需要写4个,方法参数会显很长;

而在【Spring MVC数据绑定:Controller接收请求:使用【Java Bean】接收】中就介绍了,如果参数过多的话,可以使用Java Bean去接收参数;

而在【SpringBoot的Get请求和Post请求】中就介绍了,对于Spring Boot来说,post请求接收参数时,使用@RequestBody注解更符合规范;但是,使用@RequestParam注解也是可以的;只是对于post请求来说,使用@RequestBody注解更符合规范;

为此,我们就创建一个Java Bean,以使用这个Java Bean去接收参数;

AddCategoryReq类:

 
     package com.imooc.mall.model.request;
 
     /**
      * 描述:【增加目录分类】接口的参数类;
      */
     public class AddCategoryReq {
         private String name;//目录名
         private Integer type;//目录级别
         private Integer parentId;//上一级目录的id
         private Integer orderNum;//同级目录的排序顺序
 
 
         public String getName() {
             return name;
         }
 
         public void setName(String name) {
             this.name = name;
         }
 
         public Integer getType() {
             return type;
         }
 
         public void setType(Integer type) {
             this.type = type;
         }
 
         public Integer getParentId() {
             return parentId;
         }
 
         public void setParentId(Integer parentId) {
             this.parentId = parentId;
         }
 
         public Integer getOrderNum() {
             return orderNum;
         }
 
         public void setOrderNum(Integer orderNum) {
             this.orderNum = orderNum;
         }
     }
 

然后,就可以使用AddCategoryReq去接收参数了:

一个疑问: 我们前面通过【mybatis-generator】插件,根据imooc_mall_category表,创建了Category类;可以发现,这个Category类中的属性,也都“恰巧”涵盖了【增加目录分类接口】的四个参数;;那么为什么不用直接用这个Category类去接收参数,而要重新专门创建一个AddCategoryReq类呐?

即,如果我们的SQL写的不够完美;如果使用Category接收参数,以为多余的三个字段,可能会被黑客利用,造成风险;比如下面的例子;

(2)方法,首先需要校验:参数不能为空;即,入参校验;(PS:自己做过的项目中,有的地方,这个工作是交给前端的)

(3)身份校验:因为这个接口要求【用户必须登录,而且该用户需要是管理员用户】;

(4)如果不是管理员:就返回【无管理员权限】的信息;如果是管理员用户,就去调用Service对应的逻辑;如果一切正常,就返回成功的API统一返回对象;

具体,Service的逻辑,在下部分介绍;

2.创建CategoryServiceImpl实现类、CategoryService接口;创建并实现【增加目录分类】方法:add()方法;

CategoryServiceImpl类:

 
     package com.imooc.mall.service.impl;
 
     import com.imooc.mall.exception.ImoocMallException;
     import com.imooc.mall.exception.ImoocMallExceptionEnum;
     import com.imooc.mall.model.dao.CategoryMapper;
     import com.imooc.mall.model.pojo.Category;
     import com.imooc.mall.model.request.AddCategoryReq;
     import com.imooc.mall.service.CategoryService;
     import org.springframework.beans.BeanUtils;
     import org.springframework.beans.factory.annotation.Autowired;
     import org.springframework.stereotype.Service;
 
     /**
      * 描述:分类目录CategoryService接口的实现类
      */
     @Service
     public class CategoryServiceImpl implements CategoryService {
         @Autowired
         CategoryMapper categoryMapper;
 
         /**
          * 增加目录分类
          * @param addCategoryReq
          */
         @Override
         public void add(AddCategoryReq addCategoryReq) {
             Category category = new Category();
             BeanUtils.copyProperties(addCategoryReq, category);
             //根据目录名去查询,数据库中是否已有这个名字的目录;
             Category categoryOld = categoryMapper.selectByName(addCategoryReq.getName());
             //如果查到了,说明【我们要增加了这个目录的,目录名】在数据库中已经有叫这个名字的目录了;抛出【不允许重名的业务异常】
             if (categoryOld != null) {
                 throw new  ImoocMallException(ImoocMallExceptionEnum.NAME_EXISTED);
             }
 
 
             //调用方法,插入数据
             int count = categoryMapper.insertSelective(category);
             if (count == 0) {//如果count=0,表示插入没有成功;则抛出【新增失败】的业务异常
                 throw new ImoocMallException(ImoocMallExceptionEnum.CREATE_FAILED);
             }
         }
     }
 

说明:

(1) 【BeanUtils.copyProperties(obj1,obj2)】:如果属性类型、属性名一样的话;其可以,快速把obj1的拷贝到Obj2中;

(2)【增加目录分类】的时候,也不允许重名;所以,增加之前,要看下数据库中是否已经有这个分类名了;(PS:需要在Dao层,创建一个【根据目录名,查询目录】的方法;)

(3)调用Dao层的insertSelective()去新增目录;

(4)说明:我们前面的自定义异常ImoocMallException,可以改继承RuntimeException;(这是个知识点)

有关,【自定义异常,继承Exception和RuntimeException的区别】的分析,可以参考 【附加自定义异常继承Exception和RuntimeException的区别】;

(5)在ServiceImpl中,写好方法的实现后;要在Service接口中,反向生成方法的声明;

3.启动程序,测试;

所以,正是因为【增加目录分类】接口,要求请求的参数需要放在body中;所以,Controller方法在接收请求的参数时,为了能够获取到body中的参数,我们使用了【@RequestBody】注解;


自然,我们在使用postman测试的时候,参数要放在body中;


启动项目:

我这里报错,具体原因是类没有加@Repository, categoryMapper报红,具体参考IDEA报Could not autowire. No beans of ‘ProductCategoryMapper’ type found(详细分析)._纵死侠骨香的博客-CSDN博客


PS:遇到这儿的时候,会对接口参数的位置存疑;比如:为什么大家都是POST请求,会存在以下不同;

即,为什么,大家都是post请求,有点把参数放在了url中,有点放在了body中;

(1) 对于,【用户模块】的登录接口;

(2) 对于【商品分类模块】的新增目录分类接口;

针对这个问题,在 【附加POST请求方法参数放在url中和放在body中有什么区别】中,进行了详细介绍;同时在这篇博客中,也详细总结了@Param,@RequestParam,@RequestBody这三个【与接收参数有关】的注解;