JSON parsing unexpectedly passing when sending POST to REST service - json

I have built a REST service using Spring-Boot. Everything works fine. However, when I update my curl command to POST erroneous names for my POJO variables, I get an object with default values.
My entity looks like:
#Entity
public class UserTask {
#Id
private Long userId;
private int numComplete;
private int numIncomplete;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public int getNumComplete() {
return numComplete;
}
public void setNumComplete(int numComplete) {
this.numComplete = numComplete;
}
public int getNumIncomplete() {
return numIncomplete;
}
public void setNumIncomplete(int numIncomplete) {
this.numIncomplete = numIncomplete;
}
#Override
public String toString() {
return new StringBuilder("[id : ").append(getUserId())
.append(", complete : ").append(getNumComplete())
.append(", incomplete : ").append(getNumIncomplete())
.append("]").toString();
}
}
I execute a POST against my service like so:
curl -i -X POST -H "Content-Type:application/json" -d "{ \"uId\" : \"1\", \"numC\" : \"3\", \"numI\" : \"2\" }" http://localhost:8080/user-task
The returned status for the POST is 201 and the record is written (in this case updated) in the database. When I debug, I see that the UserTask instance passed into the REST controller method has default value null for userId, 0 for numComplete and 0 for numIncomplete. I thought that the JSON parser would've failed when it couldn't unmarshal the POST JSON content as a UserTask instance?

You can use JSONSchema to validate the #RequestBody or #Valid of Hibernate Validator, so if the POJO doesn't met this validations you could Thrown a Exception of RequiredParameters for example.
yourMethod(#RequestBody #Valid UserTask)

Related

exception message for missing #RequestBody method parameter

Firstly,if you have any ideas or solution, thank you to present here.
I use #RequestBody on one of my controllers for a required parameter, I need some useful way of saying which parameter was missing if it's not there.
When there are some parameters missing, it will throw the NullPointerException, SO I create a new exception to instance of this null exception (please see the httpemun and the httphandler)
These are the primary codes which referring to this question.
my controller:
public ResponseEntity<?> createOrder(#RequestBody Cart cart) throws Exception {
// ......
}
my entity cart:
public class Cart{
private String channel
private String cartId;
private String status;
private String currency;
getters...
setters...
}
my Http emun class:
public enum HttpStatusEnum {
CRE_CART_INCOMPLETE_BODY(400,"E001","Incomplete request body","");
private HttpStatusEnum(int statusCode, String code,
String message, String detail) {
this.statusCode = statusCode;
this.code = code;
this.message = message;
this.detail = detail;
}
private final int statusCode;
private final String code;
private final String message;
private String detail;
public int getStatusCode() {
return statusCode;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
public void setDetail(String detail) {
this.detail = detail;
}
public String getDetail() {
if(detail.isEmpty()) {
return message;
}else {
return detail;
}
}
}
I also have one exception handle
#ControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
#ExceptionHandler(value = Exception.class)
#ResponseBody
public ResponseEntity<Object> defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
if(e instanceof NullPointerException)
{if(req.getRequestURL().toString().contains(HttpSourceEnum.CART.toString()) && req.getMethod().equals(RequestMethod.POST.toString())){
errorBodyVo.setMessage(HttpStatusEnum.CRE_CART_INCOMPLETE_BODY.getMessage());
errorBodyVo.setDetails(HttpStatusEnum.CRE_CART_INCOMPLETE_BODY.getDetail());
errorBodyVo.setCode(HttpStatusEnum.CRE_CART_INCOMPLETE_BODY.getCode());
}else {
errorBodyVo.setMessage(HttpStatusEnum.COMMON_MISSING_FIELD.getMessage());
errorBodyVo.setDetails(HttpStatusEnum.COMMON_MISSING_FIELD.getDetail());
errorBodyVo.setCode(HttpStatusEnum.COMMON_MISSING_FIELD.getCode());
}
httpStatus = HttpStatus.BAD_REQUEST;
}
}
right now,
my API request is:
{
"channel": "XX",
"cartId": "109",
"status": "1",
}
I receive the API response just like below:
{
"error": {
"code": "E001",
"message": "Incomplete request body",
"details": ""
}
}
but it doesn't match my expect.
if the channel is missing in my request like below:
{
"cartId": "109",
"status": "1",
}
I expect to show "Required request body content is missing: Channel" in the details:
{
"error": {
"code": "E001",
"message": "Incomplete request body",
"details": "Required request body content is missing: Channel"
}
}
How could I do that? Thanks guys!
A better approach, if you can do it, would probably be to use JSR 303 Validation, which is probably included with your existing Spring dependencies, assuming you're using recent versions.
There's a good, if quite simple, tutorial here: https://www.mkyong.com/spring-mvc/spring-rest-api-validation/ and many more online with more details.
The official Spring docs on the subject are here: https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#validation-beanvalidation
You should only need to make changes to a few classes, and can potentially remove a lot of your exception handling, depending on how much control you want, or how much you're willing to go with Spring's defaults. The basic changes you'll need to make though are:
To tell Spring what validation is required, you add one or more annotations to the Cart class, for example, if you want to ensure that the channel is specified, you could do something like:
public class Cart{
#NotNull
private String channel
private String cartId;
private String status;
private String currency;
Then, to instruct Spring to validate the Cart object before passing it to your controller, you need to add an #Valid annotation to your controller method signature:
public ResponseEntity<?> createOrder(#Valid #RequestBody Cart cart) throws Exception {
Lastly, modify the createOrder signature again to accept an Errors object:
public ResponseEntity<?> createOrder(#Valid #RequestBody Cart cart, Errors errors) throws Exception {
In the createOrder method you can then query the Errors object and respond accordingly. This could mean sending a specific response from the controller or throwing an exception to be handled by your existing exception handling mechanism.

how to add custom objectMapper for a specific spring rest controller

I have two controllers in my micro service both are POST and accepts Request body as JSON, one is working fine and another one's JSON input from some othet team and it is with root class name , so I need to write custom object mapper for this later controller, could you please guys help,
please find the codes below,
#RestController
#Slf4j
public class Controller2 {
#RequestMapping(value = "/some/update", method = RequestMethod.POST)
public String updateEmd(#RequestBody final UpdateEMDRequest updateEMDRequest) throws JsonProcessingException {
updateEMDRequest.getBookingReference()); // null now
return "success";
}
}
and the sample json is as follows,
{
"UpdateEMDRequest":{
"TransactionStatus":"SUCCESS",
"UniqueTransactionReference":"046060420",
"PreAuthReference":"040520420",
"BookingReference":"8PJ",
"CarrierCode":"AS",
"TransactionMode":"Batch",
"CallBackUrl":"www.test.com/op/update",
"Offers":[
{
"Offer":{
"traveler":{
"firstName":"AHONY",
"surname":"DNEN",
"EMD":[
"081820470"
]
}
}
}
]
}
}
UpdateEMDRequest,java
#JsonInclude(Include.NON_NULL)
public class UpdateEMDRequest {
#JsonProperty("UniqueTransactionReference")
private String uniqueTransactionReference;
#JsonProperty("TransactionStatus")
private String transactionStatus;
#JsonProperty("PreAuthReference")
private String preAuthReference;
#JsonProperty("BookingReference")
private String bookingReference;
#JsonProperty("CarrierCode")
private String carrierCode;
#JsonProperty("TransactionMode")
private String transactionMode;
#JsonProperty("CallBackUrl")
private String callBackUrl;
#JsonProperty("Offers")
private List<Offers> offers;
}
So this json is not parsed properly and updateEMDRequest's properties are null always.

rest api returns empty bracket for GET request

I implemented Rest api with Spring Boot. In my controller class, I have code to handle GET request which will return JSON if record found.
// SeqController.java
#Autowired
private SeqService seqService;
#RequestMapping(
value = "/api/seqs/{analysis_id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<SeqTb>> getSeqByAnalysisId(#PathVariable("analysis_id") String analysis_id) {
List<SeqTb> seqs = seqService.findByAnalysisId(analysis_id);
return new ResponseEntity(seqs, HttpStatus.OK);
}
I also create a bean class SeqServiceBean that extends the interface SeqService which in turn calls methods from the following JPA repository for query.
//SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
public List<SeqTb> findByAnalysisId(String analysisId);
}
Problem is when I typed the url (http://localhost:8080/api/seqs/fdebfd6e-d046-4192-8b97-ac9f65dc2009) in my browser, it returned nothing but a pair of empty brackets. I just looked in the database and that record is indeed there. What did I do wrong?
A bit late to answer this quesiton, but in case anyone else is having this issue.
This problem may be caused by the class (that we want to be displayed as a json object) missing getter and/or setter methods.
In your case the "seqTab" class may be not have getters.
Without the getters our application can not extract the fileds to build the json object.
Example :
Sample user class
public class User {
private String firstname;
private String lasttname;
int age;
public User(){
}
public User(String fname, String lname, int age){
this.firstname = fname;
this.lasttname = lname;
this.age = age;
}
}
Sample rest controller
#RestController
public class SampleRS {
#RequestMapping(value = {"/sample/{input}"}, method = RequestMethod.GET , produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> startService(#PathVariable("input") String input){
User u = new User(input,"bikila",45);
return new ResponseEntity<User>(u,HttpStatus.OK);
}
}
// If we try to hit the endpoint /sample{input} .. e.g.
Request : localhost:8080/Sample/abebe
Response :
{}
But adding the getters for the User class will solve the problem.
Modified User class with getters
public class User {
private String firstname;
private String lasttname;
int age;
public User(){
}
public User(String fname, String lname, int age){
this.firstname = fname;
this.lasttname = lname;
this.age = age;
}
public String getFirstname() {
return firstname;
}
public String getLasttname() {
return lasttname;
}
public int getAge() {
return age;
}
}
Request : http://localhost:8080/sample/abebe
Response : {"firstname":"abebe","lasttname":"bikila","age":45}
Hope that helps !
In most of case, database driver jar is not deployed in server. Check deployment assembly of project in eclipse. Also see console message to check if it is showing driver jar not found.
If this is case simply deploy this jar in deployment assembly of eclipse.
One thing, if build path has this jdbc driverjar in eclipse, main method will connect to database. But if jar is not deployed jdbc connection will not happen over http request.

How to send/accept JSON using JerseyTest Framework

I am attempting to write a simple test class that emulates a RESTful Web Service creating a Customer via a POST method. The following fails at assertEquals, I receive a 400 Bad Request response. I cannot use debugger to observe stack trace. However the console tells me the following...
INFO: Started listener bound to [localhost:9998]
INFO: [HttpServer] Started.
public class SimpleTest extends JerseyTestNg.ContainerPerMethodTest {
public class Customer {
public Customer() {}
public Customer(String name, int id) {
this.name = name;
this.id = id;
}
#JsonProperty("name")
private String name;
#JsonProperty("id")
private int id;
}
#Override
protected Application configure() {
return new ResourceConfig(MyService.class);
}
#Path("hello")
public static class MyService {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public final Response createCustomer(Customer customer) {
System.out.println("Customer data: " + customer.toString());
return Response.ok("customer created").build();
}
}
#Test
private void test() {
String json = "{" +
"\"name\": \"bill\", " +
"\"id\": 4" +
"}";
final Response response = target("hello").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(json));
System.out.println(response.toString());
assertEquals(response.getStatus(), 200);
}
}
Instead of printing response.toString(), you can read the actual body using response.readEntity(String.class). What you will find in the body is an error message from Jackson
No suitable constructor found for type [simple type, class simple.SimpleTest$Customer]: can not instantiate from JSON object (need to add/enable type information?)
At first glance your Customer class looks ok; it has a default constructor. But the really problem is that Jackson cannot instantiate it because it is a non-static inner class. So to fix it, simply make the Customer class static.
public static class Customer {}
As a general rule, when working with JSON and Jackson with Jersey, often when you get a 400, it a a problem with Jackson, and Jackson is pretty good at spitting out a meaningful message that will help us debug.

Unrecognized Property in fetching data from JSONObject in Jersey web service

I need to convert a certain JSON string to a Java object. I am using Jackson for JSON handling.
Here is my Java class:
public class RequestClass {
String email_id;
String password;
public String getEmailId() {
return email_id;
}
public String getPassword() {
return password;
}
#Override
public String toString(){
return email_id+" "+password;
}
}
Here is the web service code:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/dbconnect3")
public String connectToDbTest3(RequestClass rc) {
System.out.println("connectToDbTest3");
String email_id = rc.getEmailId();
String password = rc.getPassword();
System.out.println(email_id + " " + password);
}
This throws exception UnrecognizedPropertyException with message "Unrecognized field "email_id" (Class jaxrs.RequestClass), not marked as ignorable".
In case i am not using the annotation #JsonIgnoreProperties(ignoreUnknown = true) in my Java class, the output I am getting on line 09 is:
null myPassword
So I don't want to ignore Unrecognized field instead I want to get the value of email_id.
Please tell why It shows email_id as Unrecognized field while password is fetched successfully.
Just add #JsonProperty("email_id") before the getEmailId() like given below:
#JsonProperty("email_id")
public String getEmailId() {
return email_id;
}