How can I return Restlet response in desired format?
I am using the method:
#Get ("json")
public Address sendResponse(){
Address add = getAddress();
return add;
}
Right now I have to explicitly convert java object to a json string as a response to browser. Can't it be taken care by Restlet framework itself?
Spring MVC's Restful implementation can do it. I am looking similar implementation in Restlet too.
In fact, there are two ways to do that with Restlet:
the explicit one using JSON representations. The JSONRepresentation if you use objects from org.json or the JacksonRepresentation if you want JSON / Object mapping. You can find below an example:
#Get ("json")
public Representation sendResponse(){
Address add = getAddress();
return new JacksonRepresentation<Address>(address);
}
the implicit one using converter. In this case, it's the code you gave. You must have in your classpath an appropriate converter such as the one provided by the org.restlet.ext.jackson extension. It will detect that a JSON content needs to be returned and implicitly convert your Address object to a JSON content.
Just for hint, the json media specified in the GET annotation tells Restlet to use the associated method to handle the request when application/json is defined for conneg (content negociation) with the accept header.
Hope it helps you.
Thierry
Try setting the response type to application/json instead of just json. Normally, you need to specify the correct MIME type. As you say, if you set the MIME type correctly, other frameworks will do the conversion automatically.
Related
I am looking to get the key and value to each Json formatted call and use them as java objects such as String or Integer ,in a rest client i would enter
{
"Name":"HelloWorld"
}
And i would get back the HelloWorld mapped to its Key so far ive seen examples but im just having trouble finding out what each tag does and how to parse the body to give the above results
#POST
#Path("/SetFeeds")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#JsonCreator
public String setFeed(String jsonBody,#Context UriInfo uriInfo){
// Code to manipulate the body of the request
return response;
}
First thing you need to understand is how request body parsing is done. In JAX-RS parsing (or unmarshalling/deserializing/whatever) is done with MessageBodyReaders. There are different readers that can handle different Content-Type. For instance if you have Content-Type application/octet-stream, there is a reader that will unmarshal to byte[] or File or InputStream. So the following would work out the box
#Consumes("application/octet-stream")
public Response post(File file) {} // or `byte[]` or `InputStream`
That being said, JAX-RS implementations come with very basic readers for "easily convertible" format. For example, most requests can be converted to String, so you get that free for most Content-Types, as you are with your current code.
If we want some more complex data types, like your HelloWorld for Content-Type application/json, there is no standard reader for this. For this to work, we either need to create our own reader or use a library that comes with a reader. Luckily, the most popular JSON library in Java, Jackson, has implemented a JAX-RS provider that has a reader and a writer (for serialization).
Now depending on what server/JAX-RS implementation you are using, different implementations create light wrappers around the core Jackson JAX-RS module. If I knew the JAX-RS implementation you were using, I could recommend which wrapper to use, or you can forget the wrapper and just go with the basic Jackson module, which is
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
The above is a Maven dependency. If you are not using Maven, then basically you need to download all these jars.
You can find all of them here. Just search for them individually.
Then you need to register the provider. Again it depends on your JAX-RS implementation and how you are handling the configuration of your resource classes. I would need to see your application configuration (either web.xml or Java code) and maybe the server you are using to help with that. For the most part, the JacksonJsonProvider (which is the reader and writer) needs to be registered.
Once you have it registered then you need to understand the basics of how Jackson handles the serialization. At most basic level, Jackson looks for JavaBean properties (basic getter/setter) to match with JSON properties. For instance, if you have this bean property
public class HelloWorld {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The JSON should look like {"name": "whatever"}. The "name" key is the same as the bean property. In Bean property terms, the name of the property is all letters after the get/set with the first letter lowercased.
That's pretty much all there is to it. Now you can do
#Consumes("application/json")
public Response post(HelloWorld helloWorld) {
String name = helloWorld.getName(); // should == "whatever"
return Response.ok(helloWorld).build(); // we can also return objects
}
For more complex JSON formats, you should refer to the Jackson documentation or ask a question here on SO.
As far as the registering of the JacksonJsonProvider, if you are having trouble, please provide the information I requested, i.e. application configuration (web.xml or Java config) and the server you are using.
See Also:
JAX-RS Entity Provider to learn more about readers and writers
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?
I think the solution is to use the default binding option(off) since you are using custom marshallers.
You have two ways to achieve it:
Turn off the RestBindingMode, because otherwise the RestBindingMarshalOnCompletion in RestBindingProcessor will be registered and manually (un)marshal.
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)
Given something like:
e.g.
class User{
String name;
String someField1;
}
#Consumes("some/media-type")
class ResourceA{
public Response test(#FormParam("u") User u, #FormParam("f) String someField){
}
}
Couple of questions:
Will a single MessageBodyReader used to de-serialize User or will each field in user be de-serialized by a different reader?
Is #context required on any/all of these?
Is #FormParam required on the fields in the User class?
I'm trying to understand if the server will take the list of readers available and for each param in test, check if ANY of the readers can de-serialize that type. Or if the first reader which matches the media type consumed is expected to de-serialize all the params.
If the server is iterating through each parameter and for each parameter finding the most appropriate reader, it kind of makes sense that the input stream being passed to readFrom is the same instance, and each reader is advancing through the input stream. Is this the case or am I totally misunderstanding how the MessageBodyReader is meant to be used?
Have a look at this documentation on how entity providers are selected. In particular:
Procedure 7.2. MessageBodyReader Selection Algorithm
Obtain the media type of the request. If the request does not contain
a Content-Type header then use application/octet-stream media type.
Identify the Java type of the parameter whose value will be mapped
from the entity body. The Java type on the server is the type of the
entity parameter of the resource method. On the client it is the Class
passed to readFrom method.
Select the set of available MessageBodyReader providers that
support the media type of the request.
Iterate through the selected MessageBodyReader classes and,
utilizing their isReadable method, choose the first
MessageBodyReader provider that supports the desired combination of
Java type/media type/annotations parameters.
If Step 4 locates a suitable MessageBodyReader, then use its
readFrom method to map the entity body to the desired Java type.
Otherwise, the server runtime MUST generate a NotSupportedException
(HTTP 415 status code) and no entity and the client runtime MUST
generate an instance of ProcessingException.
#Context is not required and #FormParam does not need to be added to your bean - just to the REST resource method.
It appears it works how I suspected.
Looking at the source for RESTEasy, the MethodInjectorImpl class uses the same request instance which has an input stream. For each parameter the injector discovers the most appropriate reader.
The input stream is not touched and is advanced by each reader which parses a value from the request.
Lets say I have the following REST method:
#GET
#Path("get/{id}")
#Produces({"application/json", "application/xml"})
public Entity getEntity(#PathParam("id") int id) {
//do stuff
Entity entity = find(id);
return entity;
}
When I hit the rest endpoint with any browser by default I get back XML. Is there a way I can specify at the request which media type I want returned? or must I somehow include that information in the path?
You have to specify the Accept header with the media type you want in addition to the Content-Type header that states what is the content type of your request.
So use the Accept header instead of Content-Type header:
Accept: application/xml
You should manually prepare your response. For example if you want JSON answer, you should convert your Entity class to Json and return String.
You can use https://code.google.com/p/google-gson/ for conversion.
In fact it is your decision and your work to prepare the answer. The only thing to take in consideration that Rest works on top of HTTP, so some sort of text(JSON, XML, Plain text) is the most "friendly" format of information.
May be this example can help
RestClientWithAcceptHeader
RestClient client = new RestClient();
ClientResponse response = client.resource("http://example.com/resources/resource1").header("Accept", "application/json;q=1.0, application/xml;q=0.8").get();
// find what format was sent back
String contentType = response.getHeaders().getFirst("Content-Type");
if (contentType.contains("application/json")) {
JSONObject entity = response.getEntity(JSONObject.class); /* or use a String, InputStream, or other provider that supports the entity media type */
} else if (contentType.contains("application/xml") {
String entity = response.getEntity(String.class); /* or use a JAXB class, InputStream, etc. */
More here and here
I am using cxf as a webservice.It supports xml and json format output of the requested data.I want to know that if some exception has occured in my code then i want to return him back the error code either in xml or json format.But i dont know when to give json and xml ,it depends on the requested url that user has asked.
example
#Path("/reports/ad-view/loginId/{loginId}/publisher/")
PublisherReports getPublisherReportsAdView(
#PathParam("loginId") String loginId,
#QueryParam("fromDate") String fromDate,
#QueryParam("toDate") String toDate,
#QueryParam("filterValue") String filterValue);
If you mean you want to detect the mime type used to make the request, then you can use the #Consumes annotation to dictate which method handles which type of request. So you could write:
// Called when an XML request is made
#Path("/reports/ad-view/loginId/{loginId}/publisher/")
#Consumes("application/xml")
PublisherReports getPublisherReportsAdViewXml(...
and:
// Called when a JSON request is made
#Path("/reports/ad-view/loginId/{loginId}/publisher/")
#Consumes("application/json")
PublisherReports getPublisherReportsAdViewJson(...
Then have each variant of the getPublisherReportsAdView() method call a common method to perform the actual processing logic but still handle exceptions differently depending on the method that gets called.
An alternative approach, which doesn't require additional methods, would be to add a parameter which is annotated with the #HeaderParam annotation and use that to hold the value of the 'Content-Type' request header.
e.g.:
PublisherReports getPublisherReportsAdView(
#PathParam("loginId") String loginId,
#QueryParam("fromDate") String fromDate,
#QueryParam("toDate") String toDate,
#QueryParam("filterValue") String filterValue,
#HeaderParam("Content-Type") String contentType)
{
...
The value of contentType will likely also include charset information, for example: application/json; charset=UTF-8 so you'll need to ignore that when working out if the request contained JSON or XML.