测试开发专题:spring-boot自定义异常返回

本文探讨了在Spring Boot应用中如何定义统一的错误响应格式,并通过实例演示了如何使用@ControllerAdvice注解处理已知和未知异常,同时介绍了自定义HTTP状态码的方法。

上文测试开发专题:spring-boot统一异常捕获我们讨论了java异常以及如何使用Spring-Boot捕获异常,但是没有去说捕获异常后该如何进一步处理,这篇文章我们将对这个遗留的问题进行讨论.

统一错误响应定义

我们希望在程序发生异常的时候,能够给用户返回一个比较友好且明确的信息,对于api接口来说,一种比较好的格式是json,类似于下面这种格式

{
    "code": "10001",
    "message": "消息",
    "uri":"Get /v2/banner"
}

所以需要一个对象来描述这种数据格式:

public class UnifyResponse {
    private int code;
    private String message;
    private String requestUri;

    public UnifyResponse(int code, String message, String requestUri){
        this.code = code;
        this.message = message;
        this.requestUri = requestUri;
    }
}

错误响应处理

上文我们谈到从开发者的角度来说,异常分为已知异常和未知异常,针对不同的异常使用不同的异常处理函数进行处理,我们之前定义两个处理函数

@ControllerAdvice
public class GlobalExceptionAdvice {

    /**
     * 处理未知异常
     * @param req
     * @param ex
     */
    @ExceptionHandler(value = Exception.class)
    public void handleHttpException(HttpServletRequest req, Exception ex){
        System.out.println("发生异常了");
    }

    /**
     * 处理已知异常
     * @param req
     * @param ex
     */
    @ExceptionHandler(value = HttpException.class)
    public void handleHttpException(HttpServletRequest req, HttpException ex){

        System.out.println("发生了 HttpException");
    }
}

当未知异常发生时,需要将异常信息存储进 UnifyResponse,序列化后返回给用户

@ExceptionHandler(value = Exception.class)
    public UnifyResponse handleHttpException(HttpServletRequest req, Exception ex){
        String uri = req.getRequestURI();
        String method = req.getMethod();
        System.out.println(ex.getMessage());
        return new UnifyResponse(9999, "服务器错误", method + " " + uri);
    }

对于未知异常我们也不知道发生了什么,所以这里的code码就定义一个通用的,虽然Exception里面有message,但是这里不建议将这个异常里的message返回给用户,这位可能涉及到代码结构的一些东西,而且即使将这个信息返回给前端,他也不知道是啥问题,没什么意义,所以可以将这个message写到日志里,方便后面问题查询。

返回给用户的message可以自定义一个通用的,比如服务器错误什么的。

我们来测试一下,在Controller里抛出一个Exception:

 @RequestMapping(value = "/v2/banner", method = {RequestMethod.GET})
    public String test() throws Exception{
        throw new Exception("我抛出来的");
    }

然后再浏览器里访问,发现出错了

image-20200510110158872

这里的这个异常,看不太懂,回到异常处理方法当中去,我们直接是返回UnifyResponse对象,如果这里返回的是一个字符串,那会不会出错呢,再是试一下看看,结果还是会报这个错,也就是说无论这里返回自定义对象还是字符串,都会出现问题,那就是说spring-boot压根儿就可能不识别我们返回的东西。

在spring-boot里有一个注解@ResponseBody,可以将我们的返回值,绑定的响应的body上,我们来试一下看看能否解决这个问题。

会发现,上面改的返回String是可以成功的,但是返回UnifyResponse对象还是报错,而且报错和之前的还不一样

image-20200510114815064

刚才报的还是404的错误,现在变成了500,哪里错了呢。

我们来看一下UnifyResponse的定义,我们定义了三个私有的成员变量,但是确没有定义getter方法,那在序列化的时候是无法获取到成员变量的值的,所以报错,这里我们加上:

image-20200510152644882

然后在运行程序,访问路由:

image-20200510115919104

返回的响应就和我们预期的一样了,但是从上面的图中,看到返回的状态码是200,这显然是不对的,因为服务器已经出错了,状态码应该是500,所以这里要对状态码进行自定义。

自定义状态码

spring-boot提供了两种可以自定义状态码的方式:

注解

直接在异常处理函数上标记一个叫做

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)并制定状态码的枚举值

image-20200510152620279

重启程序,访问路由:

image-20200510121218008

可以看到状态码已经变成500了。

上面使用注解的的形式虽然能够实现我们的目的,但是这种方式不太灵活,,这里很多的response的设置都是spring-boot帮我们做了,如果需要做一些自定义就不是太方便,接下来的这种方式确可以让我们通过代码灵活的进行控制。

ResponseEntity

ResponseEntity是一个泛型类,是可以直接return回去的,可以设置很多属性,包括status、headers、body等。

用ResponseEntity来自定义已知异常处理方法的返回信息:

image-20200510152555316

然后再Controller里抛出一个NotFoundException,重新运行程序,访问路由:

image-20200510152800514

可以看到也能够返回正确的状态码。

总结

本篇文章我们介绍了,定义错误响应以及如何返回自定义的错误信息,多种方式进行定制状态码,但是我们在文章的错误信息都是硬编码在代码里的,这样很不好管理,所以下篇文章我们将介绍如何对错误信息管理,敬请关注!!!

本文链接:https://www.immortalp.com/articles/2020/05/10/1589096782703.html

欢迎大家去 我的博客 瞅瞅,里面有更多关于测试实战的内容哦!!

`spring-boot-starter-data-jpa` 是 Spring Boot 提供的一个模块,用于简化基于 JPA(Java Persistence API)的数据访问层开发。它集成了 Hibernate(默认的 JPA 实现)、Spring Data JPA 和 Spring ORM,帮助开发者快速实现数据库操作。 以下是一个完整的 Spring Boot + JPA 示例代码结构: --- ### 1. 添加依赖(Maven) ```xml <dependencies> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Starter Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- H2 Database (也可替换为 MySQL、PostgreSQL 等) --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <!-- Lombok(可选,用于减少样板代码) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> ``` --- ### 2. 配置数据库(application.yml) ```yaml spring: datasource: url: jdbc:h2:mem:testdb driver-class-name: org.h2.Driver username: sa password: password jpa: hibernate: ddl-auto: create-drop # 启动时创建表,关闭时删除表 show-sql: true database-platform: org.hibernate.dialect.H2Dialect h2: console: enabled: true # 开启 H2 控制台:http://localhost:8080/h2-console ``` --- ### 3. 创建实体类(Entity) ```java import jakarta.persistence.*; @Entity @Table(name = "users") @Data // Lombok 注解,生成 getter/setter/toString 等 @NoArgsConstructor @AllArgsConstructor public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(nullable = false, unique = true) private String email; } ``` --- ### 4. 创建 Repository 接口 ```java import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<User, Long> { // Spring Data JPA 自动实现常见方法:findAll, findById, save, deleteById 等 // 可自定义查询方法,例如: User findByEmail(String email); } ``` --- ### 5. 创建 Service 层 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserRepository userRepository; public User createUser(String name, String email) { User user = new User(); user.setName(name); user.setEmail(email); return userRepository.save(user); } public User getUserById(Long id) { return userRepository.findById(id).orElse(null); } } ``` --- ### 6. 创建 Controller ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping public User createUser(@RequestParam String name, @RequestParam String email) { return userService.createUser(name, email); } @GetMapping("/{id}") public User getUser(@PathVariable Long id) { return userService.getUserById(id); } } ``` --- ### 7. 主启动类 ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` --- ### 测试使用 1. 启动应用。 2. 访问 `http://localhost:8080/h2-console` 查看 H2 数据库(使用配置中的 JDBC URL 和账号密码)。 3. 使用 POST 请求测试创建用户: ``` POST /users?name=张三&email=zhangsan@example.com ``` 4. 使用 GET 查询: ``` GET /users/1 ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

测试老憨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值