简体   繁体   中英

Camel Rest DSL response encoding

I am currently working on a REST based java application using the new Camel REST DSL as the foundation. It mostly works except that I noticed when calling the URLs through a REST client (instead of say a browser) the JSON response is "garbled" and comes through with what I assume is the wrong encoding


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;
    }
}

A full working version can be found here: https://github.com/zwhitten/camel-rest-test

When going to the URL, {host}/testApp/data, in Chrome for example the response comes through as:

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

However using the Postman browser plugin as the client returns:

"W3siZGF0YSI6ImRhdGE6OjAiLCJ2YWx1ZSI6InZhbHVlOjowIiwiY291bnQiOjB9LHsiZGF0YSI6ImRhdGE6OjEiLCJ2YWx1ZSI6InZhbHVlOjoxIiwiY291bnQiOjF9LHsiZGF0YSI6ImRhdGE6OjIiLCJ2YWx1ZSI6InZhbHVlOjoyIiwiY291bnQiOjJ9LHsiZGF0YSI6ImRhdGE6OjMiLCJ2YWx1ZSI6InZhbHVlOjozIiwiY291bnQiOjN9LHsiZGF0YSI6ImRhdGE6OjQiLCJ2YWx1ZSI6InZhbHVlOjo0IiwiY291bnQiOjR9LHsiZGF0YSI6ImRhdGE6OjUiLCJ2YWx1ZSI6InZhbHVlOjo1IiwiY291bnQiOjV9LHsiZGF0YSI6ImRhdGE6OjYiLCJ2YWx1ZSI6InZhbHVlOjo2IiwiY291bnQiOjZ9LHsiZGF0YSI6ImRhdGE6OjciLCJ2YWx1ZSI6InZhbHVlOjo3IiwiY291bnQiOjd9LHsiZGF0YSI6ImRhdGE6OjgiLCJ2YWx1ZSI6InZhbHVlOjo4IiwiY291bnQiOjh9LHsiZGF0YSI6ImRhdGE6OjkiLCJ2YWx1ZSI6InZhbHVlOjo5IiwiY291bnQiOjl9XQ=="

The problem seems to be with the REST bind mode being "auto" and using a custom marshaller. If I set the binding mode to "json" then both the browser and client responses get garbled. If I set the binding mode to "json" and bypass the custom marshallers everything works correctly. Is there a way to configure the route to use a custom marshaller and encode the responses correctly regardless of the client?

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

You have two ways to achieve it:

  1. Turn off the RestBindingMode , because otherwise the RestBindingMarshalOnCompletion in RestBindingProcessor will be registered and manually (un)marshal.
  2. Register your own DataFormat and use it within the RestBinding automatically. You configure the REST configuration via jsonDataFormat to set the custom data format.

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

You can also create your own dataformat like so:

in your restconfiguration it will look sthg like this (see json-custom)

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

You must create a file "json-custom"

  • that's the name of the file and that file should contain the class name that implements your own way to marshal and unmarshal...
  • it must be located in your jar : META-INF\\services\\org\\apache\\camel\\dataformat

so the content of the file should be:

class=packageofmyclass.MyOwnDataformatter

The response you were receiving is JSON, but it had been encoded to base64. Taking the String from your post, I was able to decode it as:

[{"data":"data::0","value":"value::0","count":0},{"data":"data::1","value":"value::1","count":1},{"data":"data::2","value":"value::2","count":2},{"data":"data::3","value":"value::3","count":3},{"data":"data::4","value":"value::4","count":4},{"data":"data::5","value":"value::5","count":5},{"data":"data::6","value":"value::6","count":6},{"data":"data::7","value":"value::7","count":7},{"data":"data::8","value":"value::8","count":8},{"data":"data::9","value":"value::9","count":9}]

The answers above stop the response body being encoded to base64. The documentation from Apache Camel on bindingMode is illusive as to why it behaves that way when combined with explicit marshalling. Removing the explicit marshalling will return a JSON body, but you may also notice that it contains the any class names in the body. The documentation suggests that bindingMode is more for the transportation of classes and that you specifiy a type(Pojo.class) and optionally outType(Pojo.class) of your requests/responses. See http://camel.apache.org/rest-dsl.html (section Binding to POJOs Using) for more details.

Base64 is the safest way of transferring JSON across networks to ensure it is received exactly as the server sent it, according to some posts I've read. It is then the responsibility of the client to then decode the response.

The answers above do solve the problem. However, I'm not entirely convinced that mixing the data format in the service routes is such as good thing and should ideally be at a higher level of abstraction. This would then allow the data format to be changed in one place, rather than having to change it on every route that produces JSON. Though, I must admit, I've never seen a service that has change data format in its lifetime, so this really is a mute point.

We were also facing the same issue. Our DataFormat was Json .Once we implented our own custom marshaller. Camel was encoding the data to base64.I tried the approach provided by Cshculz but our CustomDataFormatter was not getting called for some reason which i couldn't figure out.

So We added .marshal(YourDataFormatter) after every Bean call.This provided us with the formatted json but in the encoded form so in the end of the route we added .unmarshal().json(JsonLibrary.Jackson) to return a raw json to the client.

sample snippet :-

.to("xxxxxxx").marshal(YourDataFormatterBeanRef)
.to("xxxxxxx").marshal(YourDataFormatterBeanRef)
.to("xxxxxxx").marshal(YourDataFormatterBeanRef)
.to("xxxxxxx").marshal(YourDataFormatterBeanRef)
.end().unmarshal().json(JsonLibrary.Jackson)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM