Camel Rest DSL响应编码

时间:2014-12-04 19:00:07

标签: java json rest jackson apache-camel

我目前正在使用新的Camel REST DSL作为基础,开发基于REST的Java应用程序。 它主要起作用,除了我注意到通过REST客户端(而不是说浏览器)调用URL时,JSON响应是"乱码"并通过我认为是错误的编码


MyRouteBuilder.java

@Component
public class MyRouteBuilder extends RouteBuilder{
    @Autowired
    LocalEnvironmentBean environmentBean;

    @Override
    public void configure() throws Exception {
        restConfiguration().component("jetty").host("0.0.0.0").port(80)
            .bindingMode(RestBindingMode.auto);

        rest("/testApp")
            .get("/data").route()
                .to("bean:daoService?method=getData")
                .setProperty("viewClass", constant(CustomeJsonViews.class))
                .marshal("customDataFormat").endRest()
            .get("/allData").route()
                .to("bean:daoService?method=getDatas")
                .setProperty("viewClass", constant(CustomeJsonViews.class))
                .marshal("customDataFormat").endRest();
    }
}

CustomeDataFormat.java

public class CustomDataFormat  implements DataFormat{
    private ObjectMapper jacksonMapper;
    public CustomDataFormat(){
        jacksonMapper = new ObjectMapper();
    }
    @Override
    public void marshal(Exchange exchange, Object obj, OutputStream stream) throws Exception {
        Class view = (Class) exchange.getProperty("viewClass");
        if (view != null)
        {
            ObjectWriter w = jacksonMapper.writerWithView(view);
            w.writeValue(stream, obj);
        }
        else
            stream.write(jacksonMapper.writeValueAsBytes(obj));

    }

    @Override
    public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
        return null;
    }
}

可在此处找到完整的工作版本: https://github.com/zwhitten/camel-rest-test

例如,在Chrome中访问网址{host} / testApp / data时,响应如下:

{
data: "Sherlock",
value: "Holmes",
count: 10
}

然而,使用Postman浏览器插件作为客户端返回:

"W3siZGF0YSI6ImRhdGE6OjAiLCJ2YWx1ZSI6InZhbHVlOjowIiwiY291bnQiOjB9LHsiZGF0YSI6ImRhdGE6OjEiLCJ2YWx1ZSI6InZhbHVlOjoxIiwiY291bnQiOjF9LHsiZGF0YSI6ImRhdGE6OjIiLCJ2YWx1ZSI6InZhbHVlOjoyIiwiY291bnQiOjJ9LHsiZGF0YSI6ImRhdGE6OjMiLCJ2YWx1ZSI6InZhbHVlOjozIiwiY291bnQiOjN9LHsiZGF0YSI6ImRhdGE6OjQiLCJ2YWx1ZSI6InZhbHVlOjo0IiwiY291bnQiOjR9LHsiZGF0YSI6ImRhdGE6OjUiLCJ2YWx1ZSI6InZhbHVlOjo1IiwiY291bnQiOjV9LHsiZGF0YSI6ImRhdGE6OjYiLCJ2YWx1ZSI6InZhbHVlOjo2IiwiY291bnQiOjZ9LHsiZGF0YSI6ImRhdGE6OjciLCJ2YWx1ZSI6InZhbHVlOjo3IiwiY291bnQiOjd9LHsiZGF0YSI6ImRhdGE6OjgiLCJ2YWx1ZSI6InZhbHVlOjo4IiwiY291bnQiOjh9LHsiZGF0YSI6ImRhdGE6OjkiLCJ2YWx1ZSI6InZhbHVlOjo5IiwiY291bnQiOjl9XQ=="

问题似乎是REST绑定模式是" auto"并使用自定义marshaller。 如果我将绑定模式设置为" json"然后浏览器和客户端响应都会出现乱码。 如果我将绑定模式设置为" json"并绕过定制marshallers一切正常。 有没有办法配置路由使用自定义编组器并正确编码响应,无论客户端?

5 个答案:

答案 0 :(得分:8)

我认为解决方案是使用默认绑定选项(关闭),因为您使用的是自定义封送器。

答案 1 :(得分:3)

您有两种方法可以实现它:

  1. 关闭 RestBindingMode ,否则 RestBindingProcessor 中的 RestBindingMarshalOnCompletion 将被注册并手动(联合)编组。
  2. 注册您自己的DataFormat并自动在RestBinding中使用它。您可以通过jsonDataFormat配置REST配置以设置自定义数据格式。

    Map<String, DataFormatDefinition> dataFormats = getContext().getDataFormats();
    if (dataFormats == null) {
        dataFormats = new HashMap<>();
    }
    dataFormats.put("yourFormat", new DataFormatDefinition(new CustomDataFormat()));
    
    restConfiguration()....jsonDataFormat("yourFormat")
    

答案 2 :(得分:0)

您也可以像这样创建自己的数据格式:

在你的restconfiguration中它会看起来像这样(参见json-custom)

    builder.restConfiguration().component("jetty")
                        .host(host(propertiesResolver))
                        .port(port(propertiesResolver))
                        .bindingMode(RestBindingMode.json)
                        .jsonDataFormat("json-custom")
                ;

您必须创建一个文件“json-custom”

  • 这是文件的名称,该文件应该包含实现自己编组和解组的方法的类名...
  • 它必须位于您的jar中: META-INF \ services \ org \ apache \ camel \ dataformat

所以文件的内容应该是:

class=packageofmyclass.MyOwnDataformatter

答案 3 :(得分:0)

您收到的回复是JSON,但已编码为base64。从你的帖子中取出字符串,我能够将其解码为:

[{ “数据”: “数据:: 0”, “值”: “值:: 0”, “计数”:0},{ “数据”: “数据:: 1”, “值”: “值:: 1”, “计数”:1},{ “数据”: “数据:: 2”, “值”: “值:: 2”, “计数”:2},{ “数据”:”数据:: 3" , “值”: “值:: 3”, “计数”:3},{ “数据”: “数据:: 4”, “值”: “值:: 4”, “计数” :4},{ “数据”: “数据:: 5”, “值”: “值:: 5”, “计数”:5},{ “数据”: “数据:: 6”, “值”: “值:: 6”, “计数”:6},{ “数据”: “数据:: 7”, “值”: “值:: 7”, “计数”:7},{ “数据”:”数据:: 8" , “值”: “值:: 8”, “计数”:8},{ “数据”: “数据:: 9”, “值”: “值:: 9”, “计数” :9}]

上面的答案阻止响应体被编码为base64。 Apache Camel在 bindingMode 上提供的文档很难说明为什么它与显式编组相结合时的行为方式。删除显式编组将返回JSON正文,但您可能还注意到它包含正文中的任何类名。文档建议bindingMode更多用于类的传输,并且您指定了请求/响应的类型(Pojo.class)和可选的outType(Pojo.class)。有关更多详细信息,请参阅http://camel.apache.org/rest-dsl.html(绑定到POJO使用部分)。

根据我读过的一些帖子,Base64是跨网络传输JSON的最安全的方式,以确保它与服务器发送的完全一样。然后客户负责解码响应。

上述答案可以解决问题。但是,我并不完全相信在服务路由中混合数据格式是好事,理想情况下应该处于更高的抽象层次。这样就可以在一个地方更改数据格式,而不必在生成JSON的每个路径上更改它。虽然,我必须承认,我从来没有见过一种在其生命周期中改变数据格式的服务,所以这真的是一个静音点。

答案 4 :(得分:0)

我们也面临着同样的问题。 我们的数据格式是Json。一旦我们实施了自定义编组器。骆驼将数据编码为base64。我尝试了Cshculz提供的方法,但由于某种原因(我不知道原因)而未调用我们的CustomDataFormatter。

因此我们在每个Bean调用之后都添加了 .marshal(YourDataFormatter)。这为我们提供了格式化的json,但是以编码形式提供了,因此在路由的最后,我们添加了 .unmarshal( ).json(JsonLibrary.Jackson)返回原始json到客户端。

示例代码段:-

.to(“ xxxxxxx”)。marshal(YourDataFormatterBeanRef)
.to(“ xxxxxxx”)。marshal(YourDataFormatterBeanRef)
.to(“ xxxxxxx”)。marshal(YourDataFormatterBeanRef)
.to(“ xxxxxxx”)。marshal(YourDataFormatterBeanRef)
.end()。unmarshal()。json(JsonLibrary.Jackson)