简体   繁体   English

Spring 和 Jackson 在异常后返回状态为 200 OK 但 JSON 不正确的响应

[英]Spring and Jackson returning a response with status 200 OK but incorrect JSON after an exception

I have a simple controller that is returning an instance of MyClass1 .我有一个简单的控制器,它返回MyClass1的一个实例。 MyClass1 contains a list of MyClass2 and use the @JsonDeserialize annotation to tell Jackson to use a custom serializer "MySerializer". MyClass1包含MyClass2的列表,并使用@JsonDeserialize注释告诉Jackson使用自定义序列化程序“MySerializer”。

public class MyClass1 {

    @JsonSerialize(contentUsing = MySerializer.class)
    private List<MyClass2> class2;

    public List<MyClass2> getClass2() {
        return class2;
    }

    public void setClass2(List<MyClass2> propertyTest2) {
        this.class2 = propertyTest2;
    }

}
public class MyClass2 {

    private String myString;

    public String getMyString() {
        return myString;
    }

    public void setMyString(String myString) {
        this.myString = myString;
    }
}
public class MySerializer extends StdSerializer<MyClass2> {

    public MySerializer() {
        this(null);
    }

    protected MySerializer(Class<MyClass2> t) {
        super(t);
    }

    @Override
    public void serialize(MyClass2 value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeObjectField("myObject", value);
        gen.writeEndObject();
    }

}
@RestController
public class MyController {

    @GetMapping(value = "/api", produces = "application/json;charset=UTF-8")
    public MyClass1 myMethod() {

        MyClass1 class1 = new MyClass1();
        MyClass2 class2 = new MyClass2();
        class2.setMyString("test string");
        List<MyClass2> class2List = new ArrayList<>();
        class2List.add(class2);
        class1.setClass2(class2List);

        return class1;
    }

}

So with this code whenever I send a get request to /api I have the response:因此,每当我向 /api 发送获取请求时,使用此代码都会得到响应:

{
    "class2": [
        {
            "myObject": {
                "myString": "test string"
            }
        }
    ]
}

However if I update the code of MyClass1 and add a property errorProperty in order to throw an exception:但是,如果我更新 MyClass1 的代码并添加属性 errorProperty 以引发异常:

public class MyClass1 {

    @JsonSerialize(contentUsing = MySerializer.class)
    private List<MyClass2> class2;

    public List<MyClass2> getClass2() {
        return class2;
    }

    public void setClass2(List<MyClass2> propertyTest2) {
        this.class2 = propertyTest2;
    }

    public int getMyError() {
        String error = null;
        error.toLowerCase();
        return 1;
    }
}

I have the following response from Spring Boot (with a status of 200 OK):我从Spring Boot收到以下响应(状态为 200 OK):

{
    "class2": [
        {
            "myObject": {
                "myString": "test string"
            }
        }
    ]
}{
    "timestamp": "2022-05-11T16:55:09.312+00:00",
    "status": 200,
    "error": "OK",
    "path": "/api"
}

I would like to understand why I'm having a response 200 ok because I would have expected a 500 internal server error since an exception was raised.我想了解为什么我会收到 200 ok 的响应,因为自从引发异常以来,我预计会出现 500 个内部服务器错误。 And also the response has 2 jsons in it so I don't think it is a correct response.而且响应中有 2 个 json,所以我认为这不是正确的响应。 Is it an issue in the way I'm using the @JsonSerialize feature or a bug from jackson.这是我使用@JsonSerialize 功能的方式的问题还是来自jackson 的错误。

Also here is the stacktrace:这里也是堆栈跟踪:

java.lang.NullPointerException: null
    at com.example.test_spring.serializeronproperty.MyClass1.getMyError(MyClass1.java:22) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_291]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_291]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_291]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_291]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:689) ~[jackson-databind-2.13.2.1.jar:2.13.2.1]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.2.1.jar:2.13.2.1]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.2.1.jar:2.13.2.1]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.13.2.1.jar:2.13.2.1]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) ~[jackson-databind-2.13.2.1.jar:2.13.2.1]
    at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1518) ~[jackson-databind-2.13.2.1.jar:2.13.2.1]
    at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1007) ~[jackson-databind-2.13.2.1.jar:2.13.2.1]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:456) ~[spring-web-5.3.19.jar:5.3.19]
    at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:104) ~[spring-web-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:290) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:183) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78) ~[spring-web-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:135) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.62.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.19.jar:5.3.19]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.62.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.19.jar:5.3.19]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.19.jar:5.3.19]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.19.jar:5.3.19]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.19.jar:5.3.19]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.19.jar:5.3.19]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.19.jar:5.3.19]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.62.jar:9.0.62]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_291]

The way you are using @JsonSerialize is fine and is not causing your issue.您使用@JsonSerialize的方式很好,不会导致您的问题。

Spring Boot would return the serialized 500 error response you are expecting if you were to force the NullPointerException somewhere outside the model object, such as the controller, but the odd behavior you are seeing is because of logic in your getter.如果您在模型对象之外的某个地方(例如控制器)强制NullPointerException ,Spring Boot 将返回您期望的序列化 500 错误响应,但您看到的奇怪行为是由于您的 getter 中的逻辑。 Simply moving that logic out of your getter will get your expected result.只需将该逻辑移出您的 getter 即可获得您的预期结果。

This odd behavior is happening during spring-webmvc's ServletInvocableHandlerMethod 's invokeAndHandle() method when it is attempting to serialize MyClass1 and is unable to resolve the NullPointerException being forced in its getMyError() method.当 spring-webmvc 的ServletInvocableHandlerMethodinvokeAndHandle()方法尝试序列化MyClass1并且无法解决在其getMyError()方法中强制执行的NullPointerException时,会发生这种奇怪的行为。

No exceptions were raised during the processing of the webRequest and so it is serializing the response with a 200 OK response.在处理webRequest期间没有引发异常,因此它使用 200 OK 响应序列化响应。 I believe an assumption is being made that best practices are being used and the response can carry itself out without meeting anymore logic as we should be dealing with a model object without further logic to process (outside your typical getters and setters).我相信正在做出一个假设,即正在使用最佳实践并且响应可以在不满足任何逻辑的情况下自行执行,因为我们应该在没有进一步处理逻辑的情况下处理模型对象(在典型的 getter 和 setter 之外)。 During the serialization of this response, unexpected logic is found in the getter by Jackson and an exception (where one is not anticipated) is raised, causing the odd behavior.在此响应的序列化过程中,Jackson 在 getter 中发现了意外的逻辑,并引发了异常(未预料到的异常),从而导致了奇怪的行为。 I believe spring-boot then intercepts the exception and serializes it and appends it to the response.我相信 spring-boot 然后拦截异常并将其序列化并将其附加到响应中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Jersey HTTP响应200可以,但是返回的内容错误(用户名/密码错误) - Jersey HTTP response 200 OK but returning content wrong (username/password incorrect) 为什么我的服务器响应状态代码是200而不是200 OK? - Why my server response status code is 200 instead of 200 ok? Spring 引导 GET 请求给出 200 OK 状态,但 Postman 返回“ø”作为响应主体 - Spring Boot GET Request gives 200 OK status, but Postman returns "ø" as response body Spring 异常处理程序不返回 JSON 响应 - Spring Exception Handler not returning JSON response 从Spring Exception Handler返回JSON响应 - Returning JSON response from Spring Exception Handler Jersey HTTP响应200可以,但是返回内容错误(检查请求) - Jersey HTTP response 200 OK but returning content wrong (checking request) OkHttp“java.net.ProtocolException:意外状态行:nullHTTP / 1.1 200 OK”在使用相同连接的“204 NO-CONTENT”响应之后 - OkHttp “java.net.ProtocolException: Unexpected status line: nullHTTP/1.1 200 OK” after a “204 NO-CONTENT” response using same connection Spring Boot - Jackson EntityNotFoundException 返回 200 而不是 500 响应 - Spring Boot - Jackson EntityNotFoundException returns 200 instead of 500 response java.util.concurrent.TimeoutException: null 在状态 200 之后好吗? - java.util.concurrent.TimeoutException: null after status 200 OK? Spring RestTemplate 用任何非 200 OK 响应交换 POST HttpClientException - Spring RestTemplate exchange POST HttpClientException with any non 200 OK response
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM