SpringBoot,控制器通过ResponseBodyAdvice返回的数据发生错误(类无法转换为字符串)

时间:2018-08-13 18:56:03

标签: java spring spring-boot

我想通过使用ResponseBodyAdvice格式化控制器的结果。 例如,控制器方法返回一个字符串(“ hello”),ResponseBodyAdvice将其格式化为Json数据{code:1,data:“ hello”}}。

这是我的控制器方法代码:

@PostMapping("login")
public String login(String username, String pwd) throws Exception {
    System.out.println(username + pwd);
    return "hhh";
}

这是我的ResponseBodyAdvice:

@ControllerAdvice
public class GlobalControllerAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        return TResult.success(o); // result is my class, it occur error
        // return JSON.toJSONString(TResult.success(o)); // result is string, it's ok
    }
}

错误消息是:

java.lang.ClassCastException: cn.tianyu.blog_api.util.TResult cannot be cast to java.lang.String
at org.springframework.http.converter.StringHttpMessageConverter.getContentLength(StringHttpMessageConverter.java:43) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.http.converter.AbstractHttpMessageConverter.addDefaultHeaders(AbstractHttpMessageConverter.java:259) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:210) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:275) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:119) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) ~[spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) [tomcat-embed-core-8.5.32.jar:8.5.32]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.32.jar:8.5.32]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_144]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_144]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.32.jar:8.5.32]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_144]

结果是 enter image description here

TResult.class

public class TResult {

private Integer code;
private String message;
private Object data;

public TResult() {
}

public TResult(Integer code, String message) {
    this.code = code;
    this.message = message;
}

public static TResult success() {
    TResult result = new TResult();
    result.setResultCode(TResultCode.SUCCESS);
    return result;
}

public static TResult success(Object data) {
    TResult result = new TResult();
    result.setResultCode(TResultCode.SUCCESS);
    result.setData(data);
    return result;
}

请注意红色箭头方向的返回类型。 最终结果类型为application / json,我设置了一个断点,方法“ ResponseBodyAdvice.beforeBodyWrite()”被调用了两次。该错误是第一次发生。组装错误消息第二次。

请帮助!非常感谢你!

5 个答案:

答案 0 :(得分:0)

现在我知道原因了。 Spring将通过方法返回值使用相应的HttpMessageConvert。 当方法返回String.class时,String将使用StringHttpMessageConvert.class来处理结果。 但是StringHttpMessageConvert仅支持String.class,并且我将其更改为TResult.class(在GlobalControllerAdvice.class中,请参阅问题),因此发生错误。 有两种解决方法: 1:重写StringHttpMessageConvert,这种方式更加复杂。 2:不要在GlobalControllerAdvice.class中更改返回类,仅更改返回值(以json格式),我使用这种方式

代码如下:

public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
    TResult result = TResult.success(o);
    // just change the value
    if (o instanceof String) {
        return JSON.toJSONString(result);
    }
    return result;
}

答案 1 :(得分:0)

 @Override
public boolean supports(final MethodParameter methodParameter,
                        final Class<? extends HttpMessageConverter<?>> aClass) {
    return !aClass.equals(StringHttpMessageConverter.class);
}

答案 2 :(得分:0)

有点无关紧要,但是我从SO中学到了我所了解的大部分内容,因为它们与所要查找的内容有些不相关,因此假设它会为您提供帮助。

ResponseEntity正是出于这个原因而构建的-用一些元数据包装响应。有了它,您就可以

  1. 添加自定义标题
  2. 处理错误
  3. 处理身份验证
  4. 在用户界面上变得轻松

这里是sample,供您入门。您的login方法看起来像

@PostMapping("login")
public ResponseEntity<String> `@ResponseBody` login(String username, String pwd) throws Exception {
    // if invalid return new ResponseEntity<>("Oops!", HttpStatus.BAD_REQUEST);
    return ResponseEntity.ok("Welcome " + username);
}

希望您能与他们一起开心,干杯。

答案 3 :(得分:0)

了解您要实现的功能,就像我最近做的一样。如果希望自定义响应及其类类型(与控制器返回类型中指定的响应类型不同),则可以在实现ResponseBodyAdvice时使用类泛型。 注意:您已将ResponseBodyAdvice与预期的 * ResponseBodyAdvice <Object> *

    @ControllerAdvice
    public class GlobalControllerAdvice implements ResponseBodyAdvice<Object> {

     @Override
        public boolean supports(MethodParameter methodParameter, Class aClass) {
            return true;
        }

        @Override
        public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
            JSONObject responseObj = new JSONObject();
            responseObj.put("code1", TResult.success(o);
            return responseObj;
        }
}

答案 4 :(得分:0)

请尝试以下代码:

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, new MappingJackson2HttpMessageConverter());
    }
}