Marshall object returned by #RequestBody - java

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) {

Related

Parsing a request body containing a quoted string as JSON in Spring Boot 2

I have an application which exposes an endpoint accepting PUT requests as a JSON-formatted string, e.g.:
PUT /my/endpoint
"some string"
My endpoint method signature is something like:
#RequestMapping(
path = "/my/endpoint",
consumes = "application/vnd.mycompany.myservice-v1.hal+json"
)
public ResponseEntity<MyResponse> myEndpoint(
#RequestBody final String myString
) {
...
}
Using Spring Boot 1 (1.5.22.RELEASE), the value of myString given the PUT example above would be the literal text some string, but under Spring Boot 2 (2.3.6.RELEASE), it's now the literal text "some string" - i.e. it seems that the input isn't strictly being parsed as JSON because the quotes are not removed.
I believe that quoted strings are valid JSON (and that unquoted strings are not), just as an object (in { and }) and a list (in [ and ]) would be.
I've taken out some extraneous detail that I don't think matters for the problem at hand (e.g. we're using CompletableFuture as a return value, I've got a #PathVariable in there as well and there's some annotation-driven validation going on), but I've left in that we're using a custom media-type in case that has something to do with it.
Any ideas how I can convince Spring Boot 2 to treat my request body as JSON properly? Unfortunately, I can't redefine the API because we already have live customers using it.
This might not be the best option but if nothing else helps at start. Instead of String let Spring handle RequestBody as an Object. So like:
public ResponseEntity<String> myEndpoint(#RequestBody final Object myString)
When using String Spring might not even use Jackson for parsing but handle the body as a String that should have all the characters in the body even content type is set to JSON.
If you do the following:
String myString2 = new ObjectMapper().readValue(myString, String.class);
you can see it resulting into myString2 having not those surrounding double quotes.
For Object Spring seems to handle it differently. It makes it to a String but seems to consider it as a JSON value (and as a String value because having surrounding double quotes) it should not have surrounding double quotes.
If you use Object and then log myString.getClass() you will see it is actually a java.lang.String
You Can create the request object
public class MyObject{
private String myStr;
}
Use the MyObject class as RequestBody object with mediaType="application/json"
In your controller
#RequestMapping(
value= "/my/endpoint", method= RequestMethod.PUT
)
public ResponseEntity<MyResponse> myEndpoint(
#RequestBody final MyObject myObj
)

Convert from JSON with Jackson Annotations

I am attempting to convert JSON into a Java object with the Play framework. I do not have easy control over the input JSON, which contains dashes in the names.
{ "field-name": "value" }
As a result, I cannot create a Java object with a default mapping to the JSON. I have a class which looks like this:
import com.fasterxml.jackson.annotation.JsonProperty;
public class Data {
#JsonProperty("field-name")
public String fieldName;
}
I know that Play 2.4 uses Jackson, and
I have a unit test which is able to populate the object from the JSON using a default Jackson ObjectMapper.
The JSON is the body of a POST request, and I attempt to use it like this:
Form<Data> form = Form.form(Data.class).bindFromRequest();
If I print form, I can that the data field is populated with the expected values. However, when I do form.get(), the returned value has a null field. (In the actual code, there are more fields, which are Strings or longs. All of them are null or 0.)
Am I attempting to customize the JSON deserialization in the wrong way? Or am I doing something else wrong?
As you've expected you've used the wrong way to deserialize. The Forms class is for PlayForms only and not for Json request. Have a look at the BodyParser and JsonActions documentation:
#BodyParser.Of(BodyParser.Json.class)
public Result index() {
RequestBody body = request().body();
Data data = Json.fromJson(body.asJson(), Data.class);
return ok("Got java object: " + data.toString());
}

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.

Can I use number as property name in Jackson mapper?

I am using Spring MVC3.2 and Jackson for JSON mapping. I want to serialize and deserialize property name with just number. Here is my class:
public Usage implement Serializable {
private String imei;
#JsonIgnore
#JsonProperty("4")
private long j2j;
#JsonIgnore
#JsonProperty("8")
private long call;
//Getters and setters
}
JSON:
{"imei":"352985052917115", "4":20, "8":10}
Controller:
#ResponseBody
#RequestMapping(value="/alert")
public JsonResult<Void> handleOverUsageAlertByDevice(#RequestBody Usage usage){
//Do something
}
But when I send the JSON to the controller, 404 Bad request error happens, saying:
The request sent by the client was syntactically incorrect.
Can I use number as Json property name?
Your answer would be appreciated.
Yes, "numeric Strings" are perfectly legal JSON names, and Jackson supports them.
So problem should not be with that but something else in request handling.
Please try by setting the content type when sending the request.
The content type should be set as "application/json".

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

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.

Categories

Resources