I use class javax.ws.rs.core.Response in my service method:
#GET
#Path("/object/{id}")
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response findObject(#Context HttpHeaders headers,
#PathParam("id") String objectId) {
Object object = getObject(objectId);
return createResponse(object, headers);
}
private Response createResponse(Object object, HttpHeaders headers) {
Response.ResponseBuilder responseBuilder = Response
.ok()
.entity(object)
.type(MediaType.APPLICATION_JSON_TYPE)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
.build();
}
And instance of Response contents hierarchy of my system which is contained in the instance object. This's object with all its children and their properties. And I want encoding values of properties in object. I need to give in response percent-encoding symbols instead of space in names for example. I need encoding whole my response including all fields. According to the UTF-8 encoding. For example, instead of value of some field 'His name: John' I need have 'His20%name%3A20%John' in my response. And similarly for all fields without exception in the whole hierarchy.
I didn't find anything better than how each field was handled manually using the method URLEncoder.encode(String s, String enc):
fieldValue = URLEncoder.encode(fieldValue, "UTF-8");
But in this case I need to manually go through all the fields that are in all the objects of my hierarchy coming in response. Maybe there is a more standard and correct way to do this?
And some fields are not string and have for example type BigDecimal. They can contain delimiters. How do I process them? Is there any way to encode the entire object response before return?
You can create separate fields/getter for encoded properties. In this way, the original values will be kept intact. You can also rely on reflection to fetch all fields and do it which will be messier than this.
class User {
private String firstName;
private String lastName;
private String encodedName;
public String getEncodedName(){
return URLEncoder.encode(this.firstName, "UTF-8”)+URLEncoder.encode(this.lastName, "UTF-8");
}
}
Related
so currently I'm working on a project where we have product objects which in turn contain "Origin" objects (containing region: String and country: String).
What I'm trying to do is a RestController which takes in an optional Origin object and does something with it (e.g. logs it).
This is what I have right now:
#GetMapping("search")
public Page<Wine> getProductByStuff(
#RequestParam(required = false) Origin origin,
/* other attributes */) {
log.info(origin); // it has a proper toString method.
}
There are two problem with this approach. First of all, when I send a request like:
http://[...]/search?origin={"region":"blah","country":"UK"}
or even the html converted string like:
http://[...]/search?origin={%22region%22:%22blah%22%44%22country%22:%22UK%22}
... it says
Invalid character found in the request target [/api/products/search?origin={%22region%22:%22blah%22%44%22country%22:%22DE%22}]. The valid characters are defined in RFC 7230 and RFC 3986.
Afaik the only valid characters Tomcat has that I need are {}. All others I've replaced with the html encoded chars and it still doesn't work.
What I did to prevent this:
#Component
public class TomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
TomcatConnectorCustomizer a = null;
factory.addConnectorCustomizers(connector -> {
connector.setAttribute("relaxedPathChars", "<>[\\]^`{|},\"");
connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|},\"");
});
}
}
(See this, which is, by the way, deprecated (at least connector.setAttribute).)
This produced:
MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type '[censored].backend.model.Origin'.
My questions are:
(How) is it possible to configure Tomcat/Spring so that they can actually accept json in the url params?
How would I format it in e.g. Postman so that it would work? Currently I'm just converting special characters by hand in the params tab of Postman.
Here is what you need to do if you want to send it as json query param.
#RestController
public class OriginController {
#GetMapping("/search")
public void getOrigin(#RequestParam(value = "origin", required = false)
Origin origin) {
System.out.println(origin);
}
}
Register a converter
#Component
public class StringToOriginConverter implements
Converter<String, Origin> {
ObjectMapper objectMapper = new ObjectMapper();
#Override
public Origin convert(String source) {
try {
return objectMapper.readValue(source, Origin.class);
} catch (JsonProcessingException e) {
//You could throw some exception here instead for custom error
return null;
}
}
}
Sending from postman
Note
My answer is not debating whether you should use POST or GET as it is not what you have asked. It is just providing one option if you want to send some payload as query param
As mentioned, don't use JSON as a path parameter.
Directly use path parameters, and convert to Origin object.
#GetMapping("search")
public Page<Wine> getProductByStuff(
#RequestParam(required = false) String region,
#RequestParam(required = false) String country, /* other attributes */) {
Origin origin = new Origin(region, country);
log.info(origin); // it has a proper toString method.
}
I need to accept JSON data in the body of a POST request. Is is possible to accept all the input JSON data that is present in the body of POST method without defining the getter and setter methods for all the keys in the JSON objects ?
#POST
#Path("/post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public postData GiveData(final postData output) {
return output;
}
My Get and Set methods ::
public class TestClass {
#JsonProperty("Type")
public String getType() {
return Type;
}
public void setType(String Type) {
this.Type = Type;
}
}
If JSON input is
{
Type : "Test"
}
Returns 200 :
But If the JSON input is
{
"Type" : "Test" ,
"Random-KeY" : "Value"
}
Returns 400 : Unable to Process JSON data
My problem is I need to accept data where we cannot expect the incoming JSON keys , so I cannot write get and set methods for all the keys, So how can I accept all the JSON objects in the body of the POST Method.
Any Suggestion could help me ?
You can mark your Class with
#JsonIgnoreProperties(ignoreUnknown = true)
This will Map only existing fields in the class and ignores remaining.
As you have unknown key/values in your json data, I suggest to use HashMap for requestbody which will server what you are wanting(i.e. no getter,setter and accept unknown values) like this:
#POST
#Path("/post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public postData GiveData(#RequestBody HashMap<String, Object> dataHashMap) {
//access the values by their keynames. You know how to get keySet of hashMap and
// iterate over them(I guess)
return output;
}
Hope this will work.
I have one REST Controller where I have written this code
#PostMapping(value = "/otp")
public void otp(#RequestBody Integer mobile) {
System.out.println(" Mobile = "+mobile);
}
And I am calling this method from Postman with the following inputs
URL : localhost:8080/otp
Body :
{
"mobile":123456
}
But I am getting the following exception
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of java.lang.Integer out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.Integer out of START_OBJECT token
If I am taking String as a parameter like this
#PostMapping(value = "/otp")
public void otp(#RequestBody String mobile) {
System.out.println(" Mobile = "+mobile);
}
And passing the inputs as
{
"mobile":123456
}
Now it is printing in the console as follows
Mobile = {
"mobile":"123456"
}
But I want only this value 123456. How to achieve my requirement?
NOTE: I don't want to create any additional POJO class or even I don't want to send the data using query/path parameter.
If you want to send your body like:
{
"mobile":123456
}
You will create another object to receive the value.
But if you only want to accept the integer value without any other object, you will not put json object in request body, but only the integer itself.
Body:
12345
You can use a class as request body:
class Request {
public Integer mobile;
}
and specify the parameter like this:
public void otp(#RequestBody Request mobile) {
...
Create a pojo class like below.
public class Mobile{
private Integer mobile;
//getter and setter
}
And then
public void otp(#RequestBody Mobile mobile)
to print value use
mobile.getMobile();
Converting process with json and #RequestBody is automatically and need you provide a class which contains proper field.If you insist to send data by request body,you could use String to receive json data as String.For example:
public void test(#RequestBody String request){
log.info(request);
}
In this way the request body you received is a String.You need some other tool to help you convert it.Like org.json,you could get more info from here HttpServletRequest get JSON POST data
But the easiest way is creating a new class to receive the data or changing #RequestBody to #RequestParam or #Pathvariable.
If you still want to use json as the request body,maybe you could create a common class A which contain lots of fields like name,phone number,email...Then after you send a request which only contains mobile,you just need to A.getMobile().In this way, even you get 100 request,you still need one POJO(but not recommend)
Just send the number in JSON.
12345
use #RequestBody to receive the int number.
it will work.
I use postman to send it in RAW JSON.
BTW, I am a beginner learning Spring Boot.
if you have org.json.JSONObject
#PostMapping(value = "/otp")
public void otp(#RequestBody String mobile) {
JSONObject obj = new JSONObejct(mobile);
System.out.print(obj.getInt("mobile"));
}
I have a Class
public class TestClass {
private String name;
private String id;
private String accountID;
}
Post Method:
#RequestMapping(value="/testclass", method=RequestMethod.POST)
public void create(#RequestBody TestClass testClass) {
//implementation using testClass
}
This works fine until the body in the request is as requires but
When a POST call with body having multiple values of a variable, the #RequestBody takes the last given value of the variable.
For Example:
POST call Body:
{
"name":"qwerty",
"id":"qw123"
"accountID":"111111",
"accountID":"222222",
"accountID":"333333",
"accountID":"444444"
}
then the testClass object has values name=qwerty,id=qw123,accountID=444444
Thus, this allows any kind of request with any number of values in the body to be processed.
Is it possible to identify this multiple values in the request so as to validate all the request prior to the implementation?
I.e I want the request to fail before reaching the code. I want to process the request only if it has single values. Also, it's not just about the given variables, the request may contain other variables as well , but the requestbody simply ignores it and takes the relevant variables alone. I want it to fail in that case too –
I have this issue:
We have a JAX.RS api like this:
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Path("get")
public User get(#QueryParam(value = "id") String identifier) {
return toUser(getUserEntry(identifier));
}
The toUser() method can return null, which in practice means that the client will see a 204 - No Content response.
Now, on the client side my code looks like this:
getWebTarget("user")
.path("get")
.queryParam("id", identifier)
.request(getMediaType())
.get()
.readEntity(SsoUser.class);
I was expecting the readEntity() to throw some kind of exception, but it actually returns null and does not complain.
Looking at the documentation, I see this:
for a zero-length response entities returns a corresponding Java object that represents zero-length data. In case no zero-length representation is defined for the Java type, a ProcessingException wrapping the underlying NoContentException is thrown.
So it appears that my User class does define a "zero-length representation". But I can't find anywhere in the documentation what this representation means.
I can understand how Java might infer that the zero-length representation is null, but I don't know where to define that.
Any insight on this?
I may be wrong about this, but I don't think Jax-RS is capable of handling null values for You. What You could do is explicitly handling that case, e.g.
#GET
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Path("get")
public Response get(#QueryParam(value = "id") String identifier) {
User maybeNull = toUser(getUserEntry(identifier));
Response response = null;
if(null == maybeNull) {
response = Response.noContent().build();
} else {
response = Response.ok(maybeNull).build();
}
return response;
}