Validation of JSON Object With #Valid and #Requestbody in SpringBoot - json

I am trying to validate a JSON body within a post method. Here is a sample code:
#PostMapping(value = "GetInfo")
public ResponseEntity<Person> getOffers(#Valid #RequestBody InfoRequest infoRequest) {
//generate person response according to inforequest JSON.
Person person = PersonGenerator.getOffers(infoRequest);
return new ResponseEntity<>(person, HttpStatus.OK);
}
When I send JSON body to get info (for ex: Name and Age) I want the program to throw an error if some extra fields are entered that are not needed for the Person class. As an example in below ExtraField. But #RequestBody and #Valid annotations are just checking for fields that have a match. Since it is filtered (afaik in this case ExtraField is filtered) I can't get full JSON to analyze infoRequest to find if any extra information was sent.
{
"Name": "sample",
"Age": "sample",
"ExtraField": "prevent",
}
I know there are some libraries to check JSON files. But first I have to know what is coming :).
If there is no annotation to see extra fields entered. How can I extract and analyze JSON file*
Note: Changing parameter type infoRequest as String is not an option for security purposes.

By default, the Spring Boot configuration will disables Jackson DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES feature. One possible solution could be enabling it again in you application.yml file as follows:
spring.jackson.deserialization.fail-on-unknown-properties=true
This will change the behaviour for Jackson ObjectMapper if you want finer-grained configuration you might use #JsonIgnoreProperties(ignoreUnknown = false) as follows:
#JsonIgnoreProperties(ignoreUnknown = false)
public class InfoRequest {
(...)
}

Related

Eliminate duplicate Json elements and retrieve element names starting with capital letters spring boot/java

I'm developing a Rest Client using Spring Boot and Spring Framework (spring-boot-starter-parent 2.1.6.RELEASE)
I have a class representing a response object as shown below:
public class ValidateResponse {
private String ResponseCode;
private String ResponseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
I'm creating a web-hook for an external api and I need to return a JSON object to for a specific endpoint (the JSON object properties must start with uppercase(s)). I'm calling returning the object from a PostMapping method nested in a RequestMapping root path:
#PostMapping("hooks/validate")
public ValidateResponse responseObj(#RequestHeader Map<String, String> headersObj) {
ValidateResponse response = new ValidateResponse("000000", "Success");
logger.info("Endpoint = hooks/validate | Request Headers = {}", headersObj);
return response;
}
However, when I hit the endpoint from postman I'm getting duplicate varialbes
{
"ResponseCode": "000000",
"ResponseDesc": "Success",
"responseCode": "000000",
"responseDesc": "Success"
}
I understand that the pojo-json conversion is handled by spring but I don't understand why the conversion is yielding duplicate variables.
Note: I know the ResponseDesc and the ResponseCode are not declared using the best standards for naming variables (camelCasing).
I've done some digging and according to the Java Language Specification
An identifier is an unlimited-length sequence of Java letters and Java digits, the first of which must be a Java letter.
and
The "Java letters" include uppercase and lowercase ASCII Latin letters A-Z (\u0041-\u005a), and a-z (\u0061-\u007a), and, for historical reasons, the ASCII underscore (_, or \u005f) and dollar sign ($, or \u0024). The $ character should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems.
So, I'm assuming its syntactically correct to define a variable using the Camelcase format [Need clarification on this].
I'm considering having to create the JSON object manually but I'd like to know the cause of this behaviour first. Any pointers are appreciated.
Jackson deserializes all the public fields that it comes across. However if you want Jackson to return the response in your expected element names (in your case elements starting with capital letters), make the fields private and annotate them with the #JsonProperty(expected_name_here). Your class file will typically looks as shown below
public class ValidateResponse {
#JsonProperty("ResponseDesc")
private String responseCode;
#JsonProperty("ResponseDesc")
private String responseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
Note: The getters and setters for these fields should be public, otherwise Jackson won't see anything to deserialize in the class.
public class ValidateResponse {
#JsonProperty("ResponseDesc")
public String responseCode;
#JsonProperty("ResponseDesc")
public String responseDesc;
//getters and setters
//constructors using fields
//empty constructor
}
This must fix your problem, however I do not know the reason as it requires deep Jackson investigation.
EDIT
I found out the reason.
The field got duplicated because in you case you had:
2 public fields named in upper case -> they are to be processed by jackson
2 getters getResponseCode and getResponseDesc -> they are to be resolved
as accessors for properties responseCode and responseDesc accordingly.
Summing this up - you have 4 properties resolved by Jackson. Simply making your fields private will resolve your issue, however I still advise using JsonProperty approach.
I added a com.google.code.gson dependency in the projects pom.xml file to configure Spring Boot to use Gson (instead of the default jackson).
The Json object returned from the hooks/validate endpoint must have its property names starting with a capital letter. Using a java class to generate the response object was resulting to camelCased property names so I resolved to create the JSON response object manually. Here's the code for creating the custom JSON object:
public ResponseEntity<String> responseObj(#RequestHeader Map<String, String> headersObj) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
JsonObject response = new JsonObject();
response.addProperty("ResponseCode", "00000000");
response.addProperty("ResponseDesc" , "Success");
logger.info("Endpoint = hooks/validate | Request Headers = {}", headersObj);
return ResponseEntity.ok().headers(responseHeaders).body(response.toString());
}
Note The JSON object is returned as a String so the response from the endpoint must have an additional header to define MediaType to inform the calling system that the response is in JSON format:
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
then add the header to the response:
return ResponseEntity.ok().headers(responseHeaders).body(response.toString());

How to force DotNetCore 2.1 Web API to output Json format? What library do I need?

I be straight to the point. I am in the process of converting ASP.Net web services into DotNetCore 2.1 services. My question is very simple. How do I get json output from a string (with a GET verb)?
I'm new at this, but almost every piece of documentation and recommendations do not work with DotNetCore.
Obviously, the following will not work:
[HttpGet]
public string Get()
{
return "{\"country_code\":\"US\",\"country_name\":\"United States\",\"region_name\":\"California\",\"city_name\":\"Los Angeles\",\"latitude\":\"34.052230\",\"longitude\":\" - 118.243680\",\"zip_code\":\"90001\",\"time_zone\":\" - 08:00\"}";
}
I just need to convert this string (or tell the client) that I want the output in json. The following does not work either - got a squiggly line under the "Json(" method and, for the life of me, can't find a reference to make it go away (I pulled it from an example, so they must be using a 3rd party json parsing library or there's a reference that I'm missing)
[HttpGet]
public JsonResult Get()
{
return Json("{\"country_code\":\"US\",\"country_name\":\"United States\",\"region_name\":\"California\",\"city_name\":\"Los Angeles\",\"latitude\":\"34.052230\",\"longitude\":\" - 118.243680\",\"zip_code\":\"90001\",\"time_zone\":\" - 08:00\"}", "application/json");
}
Ideally, I'd like to serialize an object to json, but figured I'd start with something ridiculously simple.
Anywho, if anyone can help.
If you don't already have a strongly typed model, you can build an anonymous type and return that from the controller
Simple Example.
public class MyController: Controller {
[HttpGet]
public IActionResult Get() {
var model = new {
country_code = "US",
country_name = "United States",
region_name = "California",
city_name = "Los Angeles",
latitude = 34.052230,
longitude = -118.243680,
zip_code = 90001,
time_zone = "- 08:00"
};
return Ok(model); //200 OK with content
}
}
In more complex scenarios you would get your objects from a data source.
No library needed, the framework out of the box will serialize the object(s) into JSON for you by default unless otherwise configured.
If you insist on passing a manually formatted string then use the ContemntResult object. Pass it the string and the content type.
[HttpGet]
public IActionResult Get() {
string json = "{\"country_code\":\"US\",\"country_name\":\"United States\",\"region_name\":\"California\",\"city_name\":\"Los Angeles\",\"latitude\":\"34.052230\",\"longitude\":\" - 118.243680\",\"zip_code\":\"90001\",\"time_zone\":\" - 08:00\"}";
return Content(json, new MediaTypeHeaderValue("application/json"));
}
Reference Format response data in ASP.NET Core Web API
Forcing a Particular Format
If you would like to restrict the response formats for a specific action you can apply the
[Produces] filter. The [Produces] filter specifies the response
formats for a specific action (or controller). Like most Filters, this
can be applied at the action, controller, or global scope.
[Produces("application/json")]
public class AuthorsController
The [Produces] filter will force all actions within the
AuthorsController to return JSON-formatted responses, even if other
formatters were configured for the application and the client provided
an Accept header requesting a different, available format.
Don't return string but object. So result of your actions are json string this is why you will get string in JSON and not an object
Make sure that your client is sending header "Content-Type": "application/json".
[HttpGet]
public Address Get()
{
return new Address{ CountryCode = "US"} ;
}

Spring MVC : post request and json object with array : bad request

I'm trying to retrieve parameters from a http POST request with Spring MVC.
The request contains the following json object (content-type : application/json), which itself contains an array of customObjects :
{
"globalId":"338",
"lines":[
{
"id": "someId",
"lib":"blabla",
...
}
]
}
Here's the code I'm trying to use :
#RequestMapping(method = RequestMethod.POST, value = "/valider")
#ResponseBody
public void valider(final HttpServletRequest request, #RequestParam("globalId") final String globalId, #RequestParam("lines") final MyCustomObject[] lines) {
All I'm getting is a "bad request" error (http 400).
Is it possible to separately retrieve the two parameters "globalId" and "lines" ? Or since they are in the same json object, it has to be treated has a single parameter ? How do you proceed when you have more than one parameter in a Post request ?
I think you're looking for something like `#RequestBody. Create a class to represent your JSON data. In your case, this class will contain two member variables - globalId as a string and lines as an array of the object it represents. Then in your controller method, you will use the #RequestBody annotation on this class type so that Spring will be able to convert the JSON into object. Check the examples below.
http://www.leveluplunch.com/java/tutorials/014-post-json-to-spring-rest-webservice/
JQuery, Spring MVC #RequestBody and JSON - making it work together
http://www.techzoo.org/spring-framework/spring-mvc-requestbody-json-example.html
create model object to map your Json data
class DLibrary{
int id;
String lib;
//getters/setters
}
class GLibrary{
int globalId;
List<DLibrary> lines;
//getters/setters
}
Replace your controller code with below
#RequestMapping(method = RequestMethod.POST, value = "/valider")
#ResponseBody
public void valider(#RequestBody GLibrary gLibrary) {
#RequestBody annotation will map Json to Java Object implicitly.
To achieve this spring must require jackson-core and jackson-mapper library included in your application and your Java class should have getter and setters i.e it must follow bean standards.
Indeed, I have to use #RequestBody to get the JSON object.
Quick summary, depending on how the parameters are passed in the http POST body request :
one JSON object (Content-Type: application/json), use #RequestBody to map the json object to a java object
multiple parameters (Content-Type: application/x-www-form-urlencoded), use #RequestParam for each parameter

JAX-RS / Jersey ".get(Integer.class)" and single JSON primitive (Integer) values?

I have a JAX-RS WebService with the following method:
#Path("/myrest")
public class MyRestResource {
...
#GET
#Path("/getInteger")
#Produces(APPLICATION_JSON)
public Integer getInteger() {
return 42;
}
When accessed using this snipped:
#Test
public void testGetPrimitiveWrapers() throws IOException {
// this works:
assertEquals(new Integer(42), new ObjectMapper().readValue("42", Integer.class));
// that fails:
assertEquals(new Integer(42), resource().path("/myrest/getInteger").get(Integer.class));
}
I get the following exception:
com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: A message body reader for Java class java.lang.Integer, and Java type class java.lang.Integer, and MIME media type application/json was not found
com.sun.jersey.api.client.ClientResponse getEntity
SEVERE: The registered message body readers compatible with the MIME media type are: application/json
...
The problem is just with returning single primitive values (int/boolean) or their wrapper classes. Returning other POJO classes is not the problemen so I guess all the answers regarding JSONConfiguration.FEATURE_POJO_MAPPING and JAXB annotations do not apply here.
Or which annotation should I use to describe the return type if I don't have access to its
class source?
Using ngrep I can verify that just the String "42" is returned by the webservice. Thats a valid JSON "value" but not a valid JSON "text" according to the spec. So is my problem on the client or the server side?
I tried activating JSONConfiguration natural/badgerfish according to http://tugdualgrall.blogspot.de/2011/09/jax-rs-jersey-and-single-element-arrays.html but with no success (ngrep still shows just "42"). Would that be the right path?
Any ideas are appreciated!
This is a recognized bug in Jackson, which has been touted (incorrectly in my opinion) as a feature. Why do I consider it a bug? Because while serialization works, deserialization definitely does not.
In any case, valid JSON cannot be generated from your current return type, so I would recommend creating a wrapper class:
class Result<T> {
private T data;
// constructors, getters, setters
}
#GET
#Path("/getInteger")
#Produces(APPLICATION_JSON)
public Result<Integer> getInteger() {
return new Result<Integer)(42);
}
Alternatively, you can elect to wrap root values, which will automatically encapsulate your data in a top level JSON object, keyed by the objects simple type name - but note that if this option is used that all generated JSON will be wrapped (not just for primitives):
final ObjectMapper mapper = new ObjectMapper()
.configure(SerializationFeature.WRAP_ROOT_VALUE, true)
.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
final String serializedJson = mapper.writeValueAsString(42);
final Integer deserializedVal = mapper.readValue(serializedJson,
Integer.class);
System.out.println(serializedJson);
System.out.println("Deserialized Value: " + deserializedVal);
Output:
{"Integer":42}
Deserialized Value: 42
See this answer for details on how to retrieve and configure your ObjectMapper instance in a JAX-RS environment.

Post/Put/Delete http Json with additional parameters in Jersey + general design issues

For some reason, I haven't found any normal way to do the following:
I want to Post a json object, and add additional parameters to the call (in this case, an authentication token).
This is a simple RESTful server in myUrl/server, which should give access to different resources of a "person" in the url myUrl/server/person/personCode/resourceName.
GET is easy, and requires no object, only parameters.
The problem arrises when I get to POST - how do I attach the JSON, and keep the other parameters as well?
The class (much has been removed for clarity and proprietary reasons...):
//Handles the person's resources
#Path("/person/{personCode}/{resourceName}")
public class PersonResourceProvider {
#GET
#Produces("application/json")
public String getPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Authenticates the user in some way, throwing an exception when needed...
authenticate(personCode, auth_token, resourceName);
//Returns the resource somehow...
}
#POST
#Produces("application/json")
public String postPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("resourceData") String resourceData, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Again, authenticating
authenticate(personCode, auth_token, resourceName);
//Post the given resource
}
}
Now, the GET method works perfectly, when you go to
myUrl/person/personCode/resourceName, it gives me the correct resource.
The auth_token is used with every single call to the server (for now, authentication is done by comparing with a predefined string), so it's needed. All the other parameters are provided through the path, except for the authentication token, which should not be in the path as it does not relate to the identity of the required resource.
When I get to POST, it's a problem.
I know there's a way to tell the method it consumes a JSON, but in that case, what will happen to the other parameters (auth_token is one of them)?
Should I use Multipart?
Another related question, this is the first time I've designed such a server, is this design correct?
Thanks!
I am not sure I understand what you are trying to achieve. Let me try explain a few things - hope it will be relevant to your question:
#QueryParam injects parameters which are part of your path - i.e. the part of the URL that goes after "?".
E.g. if you have a URL like this:
http://yourserver.com/person/personCode/resourceName?resourceData=abc&token=1234
Then there would be 2 query params - one named resourceData with value "abc" and the other one named token with value "1234".
If you are passing an entity in the POST request, and that entity is of application/json type, you can simply annotate your post method using #Consumes("application/json") annotation and add another parameter to your method, which does not need to be annotated at all.
That parameter can be either a String (in that case Jersey would pass a raw JSON string and you would have to parse it yourself) or it can be a java bean annotated with #XmlRootElement annotation - in that case (if you also include jersey-json module on your classpath) Jersey will try to unmarshall the json string into that object using JAXB. You can also use Jackson or Jettison libraries to do that - see this section of Jersey User Guide for more info: http://jersey.java.net/nonav/documentation/latest/json.html
Found!
Client side:
Client c = Client.create();
WebResource service = c.resource("www.yourserver.com/");
String s = service.path("test/personCode/resourceName")
.queryParam("auth_token", "auth")
.type("text/plain")
.post(String.class, jsonString);
Server side:
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
#Path("/test/{personCode}/{resourceName}")
public class TestResourceProvider {
#POST
#Consumes("text/plain")
#Produces("application/json")
public String postUserResource(String jsonString,
#PathParam("personCode") String personCode,
#PathParam("resourceName") String resourceName,
#QueryParam("auth_token") String auth_token)
throws UnhandledResourceException {
//Do whatever...
}
}
In my case, I will parse the json I get in the server depending on the resource name, but you can also pass the object itself, and make the server consume an "application/json".