【发布时间】:2020-08-13 19:06:40
【问题描述】:
我有一个 Spring Boot 2.3.2.RELEASE WebFlux 应用程序。在application.yml我有这些设置:
spring:
jackson:
property-naming-strategy: SNAKE_CASE
serialization:
write-date-timestamps-as-nanoseconds: false
write-dates-as-timestamps: true
事实上,几乎所有 JSON 响应都会正确发送回用户代理:snake case 格式,默认的除外。我的意思是框架生成的任何响应。
这是一个普通的GET:
{
"port_code": "blah",
"firm_code": "foo",
"type": "THE_TYPE",
"status": "BAR",
}
...这是对ConstraintViolationException 的自定义(在@RestControllerAdvice 中截获)响应:
{
"timestamp": 1597344667156,
"path": "/path/to/resources/null",
"status": 400,
"error": "Bad Request",
"message": [
{
"field": "id",
"code": "field.id.Range",
"message": "Identifier must be a number within the expected range"
}
],
"request_id": "10c4978f-3"
}
...最后这就是 Spring Boot 从控制器生成HTTP 404 的方式:
{
"timestamp": 1597344662823,
"path": "/path/to/resources/312297273",
"status": 404,
"error": "Not Found",
"message": null,
"requestId": "10c4978f-2" <== NOTICE HERE requestId INSTEAD OF request_id ...what the hell?!
}
这就是我在控制器中触发它的方式:
return service.findById(id).switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND)));
有没有办法告诉框架尊重杰克逊的设置?还是在配置方面我还缺少其他任何东西?
以下可用于重现响应:
- 创建一个新的 Spring Boot
2.3.3WebFlux 项目(使用start.spring.io) - 使其成为 Gradle / Java
11.x - 将
application.properties更新为spring.jackson.property-naming-strategy = SNAKE_CASE - 将
DemoApplication替换为以下内容:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import reactor.core.publisher.Mono;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RestController
@RequestMapping("/hello")
public class Ctrl {
@GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<DummyResponse> get(@PathVariable("id") final String id) {
if ("ok".equalsIgnoreCase(id)) {
return Mono.just(new DummyResponse(id));
}
return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND));
}
final class DummyResponse {
public final String gotIt;
DummyResponse(final String gotIt) {
this.gotIt = gotIt;
}
}
}
}
[x80486@archbook:~]$ curl -H "accept: application/json" -X GET http://localhost:8080/hello/ok | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 15 100 15 0 0 154 0 --:--:-- --:--:-- --:--:-- 156
{
"got_it": "ok"
}
[x80486@archbook:~]$ curl -H "accept: application/json" -X GET http://localhost:8080/hello/notok | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 140 100 140 0 0 5600 0 --:--:-- --:--:-- --:--:-- 5600
{
"timestamp": "2020-08-14T12:32:49.096+00:00",
"path": "/hello/notok",
"status": 404,
"error": "Not Found",
"message": null,
"requestId": "207921a8-2" <== Again, no snake case here :/
}
【问题讨论】:
-
当您在控制器建议中创建自定义响应时,Jackson 序列化了什么?我怀疑这可能是由于 Jackson 在使用 JavaBean 样式的属性和 Map 序列化 POJO 时表现不同。 minimal reproducible example 将有助于确定到底发生了什么。
-
问题不在控制器建议中;该响应很好(阅读:使用蛇盒)。错误的是通过
Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND))生成的。无论如何,控制器建议使用模仿DefaultErrorAttributes的自定义类。您可以在 start.spring.io 中创建一个新的 Spring Boot2.3.3WebFlux 项目,使其成为 Gradle / Java 11,然后将DemoApplication替换为我在(更新的)问题中的内容。 -
我知道问题不在于控制器建议,这就是为什么我想知道它是如何实现的,以便我可以将它与 Spring Boot 的错误控制器的工作方式进行比较。
标签: java spring-boot spring-webflux