ControllerAdvice ResponseBodyAdvice无法包含String响应

时间:2017-05-22 20:30:30

标签: rest spring-boot

不确定原因,但是当端点返回String时,ResponseBodyAdvice总是抛出Casting异常。任何其他数据类型(如Long,List)按预期工作,只有String数据有异常。有关如何解决此问题的任何建议?

import plotly
import plotly.figure_factory as FF
import plotly.graph_objs as go
import numpy as np
from scipy.spatial import Delaunay

## Draw torus
u = np.linspace(0, 2*np.pi, 15)
v = np.linspace(0, 2*np.pi, 15)
U,V = np.meshgrid(u,v)
u = U.flatten()
v = V.flatten()

x = (3 + (np.cos(v)))*np.cos(u)
y = (3 + (np.cos(v)))*np.sin(u)
z = np.sin(v)

X = (3 + (np.cos(V)))*np.cos(U)
Y = (3 + (np.cos(V)))*np.sin(U)
Z = np.sin(V)

points2D = np.vstack([u,v]).T
tri = Delaunay(points2D)
simplices = tri.simplices

plot_data=list()

torus = FF.create_trisurf(x=x, y=y, z=z, simplices=simplices, title="Torus", aspectratio=dict(x=1, y=1, z=0.3),plot_edges=True, width=1000)
plot_data.append(torus)

for i in range(10,13):
    trace0 = go.Scatter3d(
            x = (3+np.cos(v))*np.cos(u[i]),
            y = (3+np.cos(v))*np.sin(u[i]),
            z = np.sin(v),
            marker=dict(
                size=1,
                color='red',
            ),
            line=dict(
                 color='red',
                 width=4,
            ),
    )
    plot_data.append(trace0)

steps = list()

for i in range(4):
        step = dict(
        method='restyle',
        args=['visible', [False] * (4)],
        label='Time Step {}'.format(i)
        )
        step['args'][1][i] = True
        steps.append(step)

sliders = [dict(steps=steps)]

layout=dict(sliders=sliders)

fig=dict(data=plot_data,layout=layout)
plotly.offline.plot(fig,validate=False)

这是顾问:

@RestController
public class AppController {
    private static Logger logger = LogManager.getLogger(AppController.class);

    @RequestMapping(path = "/hello",
                    method = GET)
    public String hello() {
        logger.info("... AppController.hello()");

        return "hello, world!";
    }

    @RequestMapping(path = "/timestamp",
                    method = GET)
    public long timestamp() {
        logger.info("... AppController.timestamp()");
        return System.currentTimeMillis();
    }
}

Reponse类基本上是,添加时间戳并包含端点返回的数据

@ControllerAdvice
public class ResponseAdviser implements ResponseBodyAdvice<Object> {
    private static Logger logger = LogManager.getLogger(ResponseAdviser.class);

    @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {
        logger.trace("... ResponseAdviser.supports()");

        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        logger.info("... ResponseAdviser.beforeBodyWrite()");

        Response resp = new Response();
        resp.setData(body);

        return resp;
    }
}

这是堆栈跟踪:

@Data
public class Response {
    private Date   timestamp;
    private Object data;

    public Response() {
        this.timestamp = new Date();
    }
}

5 个答案:

答案 0 :(得分:4)

您可以尝试以下代码:

python

答案 1 :(得分:0)

由于您使用@RestController注释来标识您的休息端点,因此如果您要为其附加Advice,则必须使用为休息端点设计的@RestControllerAdvice注释。

答案 2 :(得分:0)

发生此错误是因为beforeBodyWrite方法的返回类型和requestmapping方法应该相同。您可以尝试始终在控制器中返回响应...

除了String之外,所有其他类型如fastJson或javabean都没有错误。所以我编写了额外的代码来处理String类型。您可以覆盖Response的{​​{1}},如果即将发布的类型为toString(),则会返回String。这可以解决您的问题

答案 3 :(得分:0)

原因:

引发ClassCastException的根本原因是您使用方法String将响应类型从Response转换为ResponseAdviser.beforeBodyWrite

当控制器请求方法的返回类型为String时,用于转换返回值的HttpMessageConverter实例为StringHttpMessageConverter

在处理控制器方法的返回值时要使用的转换器顺序如下: enter image description here

ByteArrayHttpMessageConverter无法处理String返回类型。

我们知道,在写入和刷新响应之前,应计算ContentLength(添加HTTP标头时)。 StringHttpMessageConverter.getContentLength的工具如下: enter image description here

我们可以看到,输入参数str的类型为String,但实际上,响应类型已经被修改为Response。因此,抛出了ClassCastException

解决方案:

告诉spring框架,不要在控制器方法中使用StringHttpMessageConverter处理String返回类型。您只需要在MappingJackson2HttpMessageConverter前面添加一个StringHttpMessageConverter。 示例代码:

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> 
        converters) {

        WebMvcConfigurer.super.configureMessageConverters(converters);
        converters.add(0, new MappingJackson2HttpMessageConverter());
    }

}

为什么MappingJackson2HttpMessageConverter不会抛出ClassCastException?只需看一下工具(在其超类AbstractJackson2HttpMessageConverter中): enter image description here

输入参数对象的类型为Object,因此它可以处理StringResponse

答案 4 :(得分:0)

我建议使用以下代码,覆盖 extendMessageConverters 方法并删除 StringHttpMessageConverter 类的实例的转换器,因为您根本不需要它们。

顺便说一句,如果您选择覆盖 configureMessageConverters 方法作为您的解决方案,它会起作用,但与此同时,您放入 application.yml 的属性不会对默认配置产生任何影响方法(可以通过文件加载属性)不会执行。

@Configuration
public class RestfulResponseConfigurer implements WebMvcConfigurer {
  @Override
  public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof StringHttpMessageConverter);
  }
}