RestEasy - Jax-rs - Sending custom Object in response body - java

How do I send my custom object in a response. I just want the values printed from my object.
Lets say I have an object of type Person. I am trying to send in REST response body like this.
ResponseBuilder response = Response.ok().entity(personObj);
return response.build();
But I get 500 error.
Tried this one too:
ResponseBuilder response = Response.status(Status.OK).entity(personObj);
return response.build();
Same error.
Tried setting content type as text/xml. No use.
What am I missing here? I tried googling. But not many examples out there, especially with the custom objects;
It returns fine, if I just pass a string to entity() method.

In order to return data from a Resteasy resource method you need to do several things depending on what you are trying to return.
You need to annotate your resource method with the #Produces
annotation to tell Resteasy what the return type of the method should
be.
For example, the method below returns XML and JSON depending on what the client asks for in their Accept header.
#GET
#Produces({MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML})
public Response foo()
{
PersonObj obj = new PersonObj();
//Do something...
return Response.ok().entity(obj).build();
}
Resteasy supports marshalling the following datatypes by default:
If the datatypes you wish to support are in this table then that
means they are supported by JAXB and all you need to do is annotate
your PersonObj class with JAXB annotations to tell it how to
marshall and unmarshall the object.
#XmlRootElement
#XmlType(propOrder = {"firstName", "lastName"})
public class PersonObj
{
private String firstName;
private String lastName;
//Getters and Setters Removed For Brevity
}
What if your content-type is not supported out of the box?
If you have a custom content-type that you would like to marshall then you need to create a MessageBodyWriter implementation that will tell Resteasy how to marshall the type.
Provider
#Produces({"application/x-mycustomtype"})
public class MyCustomTypeMessageBodyWriter implements MessageBodyWriter {
}
Just implement the interface and register it like any other Provider.
If you would like to read a custom content-type then you need to implement a custom MessageBodyReader to handle the incoming type and add it to the #Consumes annotation on your receiving method.

Related

Custom annotation for JAX-RS method and parameter passing to JAX-RS method from interceptor

I will introduce a little bit wider context to clarify my use case...
I would like to implement custom annotation #FilterResponse to annotate JAX-RS method in order to filter fields in JSON response. For latter purpose, I'm using Jacksons's #JsonFilter mechanism. Idea is to enable usage of dot notation to specify wanted fields in whole object graph, starting from returned entity as a root.
For example, if I have method that returns Product entity and I want to return only name field from Product and name field from associated entity CharacteristicValues within a Product, I will do something like this...
#GET
#Produces("application/json")
#Path("/{pid}")
#FilterResponse(include = {"name", "characteristicValues.name"}, responseEntityClass = Product.class)
public Response get(#PathParam("pid") Integer pid) {
Product result = productService.findSimpleProduct(pid);
return Response.ok().entity((result)).build();
}
Under the hood, in interceptor implementation for #FilterResponse annotation, I perform all necessary transformations and configuration of ObjectMapper.
Further, inside interceptor I generate some objects that can be useful in the rest of request processing (in Services, in DAOs, etc), so I would like to pass that objects created in interceptor (for example, my FieldTree object created from metadata specified in #FilterResponse annotation) to intercepted JAX-RS method... And that is my problem right now.
UPDATE: For example, passed FieldTree could be used to reconstruct JPA EntityGraph in order to fetch only preferred fields from database.
Here is pseudo-cod of interceptor to illustrate idea...
#FilterResponse
#Interceptor
public class FilterResponseInterceptor {
#AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
List<String> fieldsToRetain = getIncludeListFromFilterResponseAnottationIn(context);
FeildTree fieldTree = createFieldTreeFrom(fieldsToRetain);
passFieldTreeToMethodInContext(fieldTree, context);
Object response = context.proceed();
return filteredResponseWithFields(response, fieldsToRetain);
}}
When I try to declare paramter which I want to pass to intercepted JAX-RS method from interceptor, as shown below...
#GET
#Produces("application/json")
#Path("/{pid}")
#FilterResponse(include = {"name", "characteristicValues.name"}, responseEntityClass = Product.class)
public Response get(#PathParam("pid") Integer pid, FieldTree requestedFieldTree) {
Product result = productService.findSimpleProduct(pid);
return Response.ok().entity((result)).build();
}
...I'm getting following error...
RESTEASY002010: Failed to execute: javax.ws.rs.NotSupportedException: RESTEASY003200: Could not find message body reader for type: class app.fieldsfiltering.FieldTree of content type: */*
Is there any way to make RESTEasy to ignore some paramters in JAX-RS method, or some other way to perform parameter passing to intercepted JAX-RS method?
Sorry for my lengthily question.
UPDATE2: Some clarifications of my idea... I didn't mention that I would like to avoid "trivial" DTOs that are purely selection of some fields from domain entity without change of the structure. So... EntityGraph for example will be created in DAO or Service layer... But, i feel that "metadata" necessary for EntityGraph creation (e.g. preferred field graph or FieldTree as I called it) should be placed on JAX-RS method. Why? In that case, I would pass FieldTree to Service logic... then, I would create appropriate EntityGraph and eagerly fetch domain entity with all fields defined in FieldTree.. then, I would return domain entity to JAX-RS method and retain only fields defined in FieldTree in JSON response in order to avoid LazyInitialization exception that will be thrown if Jackson try to parse some lazy field in entity object.

Marshall object returned by #RequestBody

I have XML being fed into a REST controller and I use the #RequestBody annotation to transform that into a Java Object. I need to save the whole XML to a database column in addition to some specific elements from the Java Object. I am marshalling the Java object explicitly to accomplish this and that seems to be duplicate work. Is there a way to get the raw XML in addition to the parsed object while using the #RequestBody annotation?
Yes, there is. Let's say this is your controller method:
public Response yourControllerMethod(#RequestBody YourDTO dto) {
Simply change your #RequestBody parameter type to String and you'll get the raw body of the request:
public Response yourControllerMethod(#RequestBody String rawPayload) {
Or you can even get both:
public Response yourControllerMethod(#RequestBody YourDTO dto, #RequestBody String rawPayload) {

Jersey, JSR 303 validation - custom "path" and "invalidValue" in ValidationError

I am using Jersey (JAX-RS) and I'm trying to implement a validation. I have a problem with a response returned by my application when a validation error occurs. Now the response looks like this:
[{
"message": "Custom message",
"messageTemplate": "{custom.message.template}",
"path": "SomeJerseyResource.resourceMethod.arg0.names[0]",
"invalidValue":"[value1, value2]"
}]
where "SomeJerseyResourceClass.resourceMethod" is a JAX-RS resource:
public class SomeJerseyResource {
#POST
#Path("/path")
public Response resourceMethod(#Valid RequestModel request) {
/** method body **/
}
}
and validation constraint is assigned to a getter in RequestModel:
public class RequestModel {
private List<String> names = new ArrayList<>();
#MyConstraint
public List<String> getNames() {
return tags;
}
}
I have a custom ConstraintValidator, where I validate each element of that List.
Problem
I don't want to include resource and method name in "path" field of the response. Instead of
SomeJerseyResource.resourceMethod.arg0.names[0] I want arg0.names[0] only. Client doesn't know about server classes and methods, and he wouldn't be able to properly assign errors to fields when he receives response like that.
I want to customize "invalidValue" field of a response. More specifically, to have only invalid element value, not the whole list in that field.
I didn't find any easy way to do that. Do you have any ideas?
You can just write an ExceptionMapper<ConstraintViolationException> to return the Response of your liking. Jersey uses an ExceptionMapper<ViolationException>. ConstraintViolationException extends from ViolationException, so you're mapper is more specific, and would take precedence in the choosing of the mapper. Jersey's mapper, returns the response as a ValidationError, that's why the body is how it is. But you can make it whatever you want.
If you just want the invalidValue list, then just iterate through the ConstraintViolations from ContraintViolationException.getConstraintViolations(), and get the invalidValue from the ConstraintViolation.

Jersey JAX-RS Client XML to java.util.List deserialization

I am trying to access a JAX-RS Service (jersey implementation) which is returning me a java.util.list of Employees in XML format.
The Service method signature looks like this:
#GET
#Path("/getEmployeeListXML")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_XML)
public List<EmployeeXML> getEmployeeListXML(#QueryParam("id") String id){
//Some code which returns a List<EmployeeXML>
}
The format of the XML returned is like this:
<employeeXMLs>
<employeeXML>
<empId>1</empId>
<empName>John</empName>
<empAge>35</empAge>
<empSex>Male</empSex>
</employeeXML>
<employeeXML>
<empId>2</empId>
<empName>Lisa</empName>
<empAge>23</empAge>
<empSex>Female</empSex>
</employeeXML>
</employeeXMLs>
For accessing this from my jersey Client, I am using this code:
List<EmployeeXML> empListXML = (List<EmployeeXML>)service.path("rest").path("GetService").path("getEmployeeListXML").accept(MediaType.APPLICATION_XML).get(EmployeeXML.class);
This is not correct since the return type should be a list but in the get method, presently I am trying to retrieve a single object. I am not sure how to retrieve the List from the client here :(
I am getting this exception:
unexpected element (uri:"", local:"employeeXMLs"). Expected elements are <{}employeeListXML>,<{}employeeXML>
Please help me out to make this work.
Thanks,
You can use the GenericType class to fetch a list of objects:
List<EmployeeXML> empListXML = (List<EmployeeXML>)service.path("rest").path("GetService").path("getEmployeeListXML").accept(MediaType.APPLICATION_XML).get(new GenericType<List<EmployeeXML>>(){});
You need to use a 'supertype token' to define the return type in your client class:
List<EmployeeXML> empListXML = service
.path("rest")
.path("GetService")
.path("getEmployeeListXML")
.accept(MediaType.APPLICATION_XML)
.get(new GenericType<List<EmployeeXML>>() {});
The supertype token is required in order to 'retain' generic parameter information that Jersey will use when deserializing the server response.

RESTEasy client: reconstructing an object

I'm playing with RESTEasy to consume REST services, and I'm trying it out with Twitter's search API.
So I create this interface:
public interface SimpleClient {
#GET
#Path("search.json")
#Produces("application/json")
ClientResponse<Set<String>> getSearchResults(
#QueryParam("q") String hashtag,
#QueryParam("result_type") String resultType
);
}
and called it with:
SimpleClient client =
ProxyFactory.create(SimpleClient.class,"http://search.twitter.com/");
ClientResponse<Set<String>> response =
client.getSearchResults("#wowodc","recent");
System.out.println(response.getEntity(Set.class));
But I'm getting:
ClientResponseFailure: Unable to find a MessageBodyReader of content-type application/json;charset="utf-8" and type interface java.util.Set
I have tried using a POJO instead of java.util.Set, but I'm getting the same kind of exception. The only thing that didn't throw an exception is using String instead of Set.
By reading some example code on the Web, I was thinking that Set or a POJO as the entity type would have work, but it doesn't for me. The query to Twitter did return valid results.
You need to make sure you include a RESTEasy provider that can unmarshal JSON responses. There's a one based on the Jackson parser library that you can use, it's described in the docs here.

Categories

Resources