英文:
Spring Boot default error message(s) is not honoring Jackson's Property Naming Strategy
问题
你的Spring Boot应用中,要让框架遵循Jackson的设置,以使用Snake Case命名策略,你可以尝试在application.yml
中添加以下配置:
spring:
jackson:
property-naming-strategy: SNAKE_CASE
这应该会使大多数JSON响应以Snake Case格式返回给用户代理,包括框架生成的响应。如果这个设置没有生效,你可能需要检查是否有其他配置或拦截器干扰了Jackson的行为。
英文:
I have a Spring Boot 2.3.2.RELEASE
WebFlux application. In the application.yml
I have these settings:
spring:
jackson:
property-naming-strategy: SNAKE_CASE
serialization:
write-date-timestamps-as-nanoseconds: false
write-dates-as-timestamps: true
Indeed, almost any JSON response is sent back to the user-agents correctly: in snake case format, except for the default ones. What I meant by this, is any response generated by the framework.
This is a usual GET
:
{
"port_code": "blah",
"firm_code": "foo",
"type": "THE_TYPE",
"status": "BAR",
}
...this is a custom (intercepted within a @RestControllerAdvice
) response for a ConstraintViolationException
:
{
"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"
}
...and finally this is how Spring Boot generates an HTTP 404
from the controller:
{
"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?!
}
> This is how I'm triggering that in the controller: return service.findById(id).switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND)));
Is there any way to tell the framework to honor Jackson's settings? Or is there anything else I'm missing configuration-wise?
The following can be used to reproduce the responses:
- Create a new Spring Boot
2.3.3
WebFlux project (usingstart.spring.io
) - Make it Gradle / Java
11.x
- Update
application.properties
withspring.jackson.property-naming-strategy = SNAKE_CASE
- Replace
DemoApplication
with the following:
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 :/
}
答案1
得分: 2
Spring Boot的错误控制器将一个Map
交给Jackson进行序列化,而您的控制器建议(serializing a Java object)则将一个Java对象进行序列化。在序列化Java对象时,Jackson会根据JavaBean风格的属性进行序列化,并使用属性命名策略来决定JSON中的每个属性应该如何呈现。在序列化Map时,键(key)不被视为属性(properties),因此属性命名策略不起作用,Jackson会按原样序列化键(key)。
目前来看,如果您想自定义在序列化时映射键的格式,您将需要配置您的ObjectMapper
以使用自定义的StringKeySerializer
。
英文:
Spring Boot's error controller gives Jackson a Map
to serialize whereas your controller advice is serialising a Java object. When serializing a Java object, Jackson serializes it based on JavaBean-style properties and uses the property naming strategy to decide how each of those properties should appear in the JSON. When serialising a map, the keys aren't treated as properties so the property naming strategy has no effect and Jackson serialises the keys as-is.
As things stand, if you want to customise the format of map keys as they're serialized you'll have to configure your ObjectMapper
to use a custom StringKeySerializer
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论