springboot 学习第1期 - 创建工程

131 阅读2分钟

创建项目

进入网址 start.spring.io/

然后可以使用 IDEA 打开工程。

创建Controller

package com.congvee.springboot_hello.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/hello")
public class HelloController {

    @GetMapping
    public String hello() {
        return "Hello, World!";
    }

}

接口调用

直接启动项目,默认运行在端口 8080 上,使用 postman 进行接口调试

如果想要自定义端口,可以在 application.properties 配置文件中添加 server.port=8081

响应统一结构

响应体的一般结构是:

{
  "code": 0,
  "message": "success",
  "data": { ... }
}

code 0 表示成功,非 0 表示异常,可以自定义一些异常码。

可以这样定义统一类型:

package com.congvee.springboot_hello.common;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class ApiResponse<T> {

    private int code;
    private String message;
    private T data;

    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(0, "success", data);
    }

    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null);
    }
    
  
    public static <T> ApiResponse<T> error(int code, String message, T detail) {
        return new ApiResponse<>(code, message, detail);
    }
}

使用lombok注解需要额外引入依赖:

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <scope>provided</scope>
  <optional>true</optional>
</dependency>

使用:

@GetMapping
public ApiResponse<String> hello() {
    return ApiResponse.success("Hello World!");
}

接口调用:

统一转换

ResponseBodyAdvice 可以在正常响应到客户端之前做一些统一处理,我们可以将ApiResponse放在这里统一处理,这样Controller的返回值就不用每次都写ApiResponse了。

@Slf4j
@ControllerAdvice
public class ResponseWrapper implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        ObjectMapper mapper = new ObjectMapper();

        try {
            // String 类似特殊处理
            if (body instanceof String) {
                return mapper.writeValueAsString(ApiResponse.success(body));
            }

            return ApiResponse.success(body);
        } catch (Exception e) {
            // 这里抛出的异常是不会走全局异常处理器的
            try {
                ApiResponse<?> errorResp = ApiResponse.error(50000, "响应序列化失败");
                String json = mapper.writeValueAsString(errorResp);

                // 直接写到输出流
                response.getBody().write(json.getBytes(StandardCharsets.UTF_8));
                response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            } catch (IOException ex) {
                // 如果连写异常响应都失败,只能默默记录日志
                log.error("Failed to write error response", ex);
            }

            // 告诉 Spring:我已经写完,别再处理了
            return null;
        }
    }
}

这样Controller又回到了这种写法:

@RestController
@RequestMapping("/api/hello")
@Validated
public class HelloController {

    @GetMapping
    public String hello() {
        return "Hello World!";
    }

}

接口调用: