统一结果集和异常处理
查询参数的接收
HTTP请求:
URL/URI:
统一资源标识符(Uniform Resource Identifier,URI)是一个用于标识某一互联网资源名称的字符串。
URL 的全称为 Universal Resource Locator
URI >= URL
比如:
- /users:URI,
- http://localhost:8989/users :URL 或者URI
请求参数:
- 查询参数:/users?id = 1 & userName=Rose
- 路径参数:/users/1/Rose
对于Rest接口,比较推荐使用路径参数。
SpringMVC中的Controller的方法,默认接收的查询参数,如果你要接收路径参数,必须加@PathVariable注解。
路径参数如果参数名不一致:
查询参数,默认无需额外处理,是默认的。但如果参数名不一致,也需要加个注解@RequestParam
举例:使用查询参数根据id查询用户:
/**
* 查询参数示例
* @param id
* @return
*/
@GetMapping("/user/query")
// public User getById2(Integer id){
public User getById2(@RequestParam("id") Integer id){
System.out.println(id);
User user1 =new User();
user1.setId(id);
user1.setUserName("Rose");
user1.setAge(18);
return user1;
}
浏览器请求:
http://localhost:8989/user/query?id=123
资源路径冲突
如果资源路径重复,将会报异常,程序无法启动:
在代码中发现:
映射对:
key:value
资源路径:controller方法
如果启动失败,则除了控制台异常,没有停止按钮。
统一结果集
现在存在的问题:
- 接口返回的结果不统一,前端不好处理,一般我们都返回json
- 数据格式太简单,不适合业务.
企业中一般的格式:
{
结果码:20000,
提示消息:"用户名或密码不正确",
数据:{
id:1,
userName:Rose
}
}
我们需要在Java中定义统一结果集对象类:
package cn.bobohost.market.pojo;
import lombok.Data;
/**
* 统一结果集对象
* Dta:数据传输/转换对象
*/
@Data
public class ResultDto<T> {
/**
* 响应的数据状态码
* 20000 : 表示本次请求一切正常,响应也正常
* 50000 : 本次操作出现的错误(具体错误信息需要有针对性的返回给前端)
* 用户名或密码错误
* 没有任何员工数据
* 根据员工id删除账号失败
*/
private Integer code = 20000;
/**
* 每个状态对应的本次操作的描述信息
* 20000 :操作成功
* 50000 :操作失败
*/
private String msg ;
/**
* 本次请求返回给客户端的数据
*/
private T data;
/**
* 封装分页相关信息
*/
private Integer pages;
private Long total;
//无参构造器(默认自带)
public ResultDto() {
}
//有参构造器(自定)
public ResultDto(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
/**
* 调用成功之后的结果
* @param msg
* @param data
* @return
* @param <T>
*/
public static <T> ResultDto<T> success(String msg,T data) {
return new ResultDto<>(20000,msg,data);
}
/**
* 失败的结果
* @param code
* @param msg
* @return
* @param <T>
*/
public static <T> ResultDto<T> error(Integer code,String msg) {
return new ResultDto<>(code,msg,null);
}
/**
* 自定义结果
* @param code
* @param msg
* @param data
* @return
* @param <T>
*/
public static <T> ResultDto<T> of(Integer code, String msg, T data) {
return new ResultDto<>(code,msg,data);
}
}
将UserController复制一份UserDemoController,编写代码:
package cn.bobohost.market.web.controller;
import cn.bobohost.market.pojo.ResultDto;
import cn.bobohost.market.pojo.User;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* 用户的Controller
*/
@RestController
//请求映射:如果放到类上,那么将来请求的时候,规则:类上的这个路径+方法上的路径。因此我们可以抽取前缀,一般不同模块有不同前缀
@RequestMapping("/userdemo")
public class UserDemoController {
/**
* 所有Controller的路径不能重复
* @param user
* @return
*/
@PostMapping
public ResultDto add(@RequestBody User user){
//服务端控制台打印
System.out.println(user);
//返回结果给客户端
return ResultDto.success("保存成功",null);
}
/**
* 更新用户
* @param user 用来封装数据的消息体
* @param id 用户的唯一编号
* @return
*/
@PutMapping("/{id}")
public ResultDto update(@RequestBody User user,@PathVariable Integer id){
//服务端控制台打印
System.out.println(user);
//返回结果给客户端
return ResultDto.success("修改成功",null);
}
/**
* 删除
* 客户端:/user/1
* @param id
* @return
*/
@DeleteMapping("/{id}")
public ResultDto remove(@PathVariable Integer id){
System.out.println(id);
//返回结果给客户端
return ResultDto.success("删除成功",null);
}
/**
* 查询所有的用户列表
* @return
*/
@GetMapping
public ResultDto list(){
List<User> userList =new ArrayList<>();
//添加元素---自己模拟两个用户
User user1 =new User();
user1.setId(1);
user1.setUserName("Rose");
user1.setAge(18);
User user2 =new User();
user2.setId(2);
user2.setUserName("Jack");
user2.setAge(88);
//将用户添加到列表
userList.add(user1);
userList.add(user2);
//返回列表
return ResultDto.success("查询成功",userList);
}
/**
* 根据id查询一个用户
* @param id
* @return
*/
@GetMapping("/{id}")
public ResultDto getById(@PathVariable Integer id){
System.out.println(id);
User user1 =new User();
user1.setId(id);
user1.setUserName("Rose");
user1.setAge(18);
return ResultDto.success("查询成功",user1);
}
}
由于ResultDTO是通用的,因此我们可以放到common包中,拖动重构:
重构之后:
统一分页结果类:
package cn.bobohost.market.common.pojo;
import lombok.Data;
import java.util.List;
/**
* 分页返回结果
*/
@Data
public class ResultPageDto<T> {
private Long total;//总记录数
private List<T> rows;//当前页的数据列表
public ResultPageDto(Long total, List<T> rows) {
this.total = total;
this.rows = rows;
}
public static <T> ResultPageDto<T> of(Long total, List<T> rows){
return new ResultPageDto(total, rows);
}
}
Java的异常
Java语言的健壮性的机制,会导致操作终止。
表面现象上看,是出错了了效果。实际上,异常不是错误,很多时候是我们主动抛出来。
当程序出现意外或者主动的异常时,控制台会打印很多字符,描述异常的信息和发生异常的代码的位置。
Java的异常体系:
在idea中要想找任何东西(类、代码、方法、idea的功能),可以连续按三下shift键:
比如找maven的配置:
找throwable类:
throwable类是异常的祖宗,代表了所有异常。
error异常类:一般是虚拟机使用的,系统级别的异常(cpu问题、内存不足),一般不用。
Exception:普通异常,我们业务上会用的
RuntimeException:运行时异常,代码在运行时发生的异常。继承了Exception,功能更强大,一般我们用这个。
一旦出现异常的时候,我们要拦截处理这个异常,如果不处理,则会抛到控制台打印。
回到业务:
在操作数据的时候一定会成功吗?不是!
什么原因造成失败?代码有问题、数据库问题
下面以查所有的为例:
制造除零异常:
作为程序员,至少会调试代码。
比如这里,可以点击异常提示的行,直接跳转到某行。
第二种方式,主动抛出异常:
处理异常
业务中,可能会发生不可预测的异常以及自定义抛出的异常。
如果发生了异常,则当前操作会终止,比如启动的时候抛异常,启动就失败了;如果启动成功,但发请求的时候异常了,那么当前请求失败了。
默认情况下,当操作发生异常时,控制台会打印异常,并且SpringMVC会用默认的方式处理异常,返回响应给客户端。
/**
* 查询所有的用户列表
* @return
*/
@GetMapping
public ResultDto list(){
//主动制造异常:
// 1)触发jdk内置的异常
// int d =1/0;
//2) 主动抛出异常
//欺骗了编译器
// if(true){
// throw new RuntimeException("我主动制造的异常");
// }
//捕捉处理异常:try如果有异常,就抓取;
//try是用来捕获
try {
List<User> userList =new ArrayList<>();
//添加元素---自己模拟两个用户
User user1 =new User();
user1.setId(1);
user1.setUserName("Rose");
user1.setAge(18);
User user2 =new User();
user2.setId(2);
user2.setUserName("Jack");
user2.setAge(88);
//将用户添加到列表
userList.add(user1);
userList.add(user2);
//制造异常
// int d =1/0;
if(true){
throw new RuntimeException("我主动制造的异常");
}
//返回列表
return ResultDto.success("查询成功",userList);
//catch是用来抓取特定异常,可以处理.异常只能抓同样的或比自己辈分小的异常。
// }catch (ArithmeticException e){
}catch (Exception e){
System.out.println("出现了异常:"+e.getMessage());
return ResultDto.error(50000,"查询失败");
}
//返回列表
// return ResultDto.success("查询成功",userList);
}
统一异常处理
我们每一个方法都自己处理异常,非常麻烦。是否可以统一处理异常,简化代码呢?
可以!
机制:AOP(面向切面的编程):不改变原来代码就可以增强原来的代码。
SpringMVC帮咱提供了AOP的功能机制,可以实现AOP。我们利用这个东东,来实现统一异常处理。
我们可以编写一个通知(增强)类,来统一处理异常:
ControllerExceptionAdvice
package cn.bobohost.market.web.advice;
import cn.bobohost.market.common.pojo.ResultDto;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 用来处理Controller里面的异常的增强类
* RestControllerAdvice:专门用来处理拦截contrller中的异常的类
*/
@RestControllerAdvice
public class ControllerExceptionAdvice {
/**
* 方法名随便
* @ExceptionHandler默认处理所有异常,会将异常对象扔到方法的参数中
* @param e
* @return
*/
@ExceptionHandler
public ResultDto handleThrowable(Throwable e){
//记录日志(也可以发邮件、发短信等)
// log.error(e.getMessage(),e);
System.out.println("操作失败,错误是:"+e.getMessage());
//返回响应结果
return ResultDto.error(50000,"操作失败!");
}
}
面的异常的增强类
-
RestControllerAdvice:专门用来处理拦截contrller中的异常的类
*/
@RestControllerAdvice
public class ControllerExceptionAdvice {/**
- 方法名随便
- @ExceptionHandler默认处理所有异常,会将异常对象扔到方法的参数中
- @param e
- @return
*/
@ExceptionHandler
public ResultDto handleThrowable(Throwable e){
//记录日志(也可以发邮件、发短信等)
// log.error(e.getMessage(),e);
System.out.println(“操作失败,错误是:”+e.getMessage());
//返回响应结果
return ResultDto.error(50000,“操作失败!”);
}
}