Search code examples
jsonspringspring-mvcsencha-touch-2

JSON Payload not Parsed on POST Spring 4.x MVC


Folks, I have found that many of the examples (in fact all) are designed around using the @ResponseBody like this post does (click here). However I just can't seem to put it all together with so many seemingly conflicting ways to do one simple thing. I confess, my last usage of Spring was limited to 2.5, and sadly, that makes me only as good as my last version. I have to write my own services for the project, and Spring is obviously the way to go. I used this excellent set of examples (click here) to put together the initial concept and everything was working fine from the REST proxy in my Sencha Touch application. Or so I thought. Until I began PUTing and POSTing JSON payloads to the Controller. Suddenly there was no data mapped from the HTTP Request.

Here is my controller, please keep in mind this is Spring 4 and according to what I have read @ReponseBody was used in Spring 3 and that functionality is now delegated because of the @RestController used instead of @Controller that Spring 3. So configuration details are sketchy for a person like me, one who has a good grasp of Java, but not deep experience with frameworks like Spring. I ask you for details regarding "where" beans are placed for configuration because with 2.5, it was an XML zoo for configuration. It seems like naming conventions are a rule now for associations that before were left to XML files. They were in the past to an extent, but not as they seem to be now.

     @RequestMapping(value = "/create/{wellTestID}", method = RequestMethod.POST,headers="Accept=application/json")
     public List<WellTest> getWellTestByWellTestID(@PathVariable Long wellTestID, 
             @RequestParam(defaultValue = "99999")  Long wellFacilityID, 
             @RequestParam(defaultValue = "101")    int operatorPersonID, 
             @RequestParam(defaultValue = "67845")  int durationSeconds, 
             @RequestParam(defaultValue = "100")    long grossRate, 
             @RequestParam(defaultValue = "400")    long waterCut, 
             @RequestParam(defaultValue = "30")     long percentGas, 
             @RequestParam(defaultValue = "500")    long temperature, 
             @RequestParam(defaultValue = "60")     long flowLinePressure, 
             @RequestParam(defaultValue = "50")     long casingPressure, 
             @RequestParam(defaultValue = "0")      int allocation, 
             @RequestParam(defaultValue = "3")      int wellTestTypeID,  
             @RequestParam(defaultValue = "you")    String createUser,
             @RequestParam(defaultValue = "me-me")  String lastModifiedUser,
             @RequestParam(defaultValue = "1-1-2014") String startDate, 
             @RequestParam(defaultValue = "1-1-2014") String createDate, 
             @RequestParam(defaultValue = "1-1-2014") String lastModifiedDate, 
             WellTest wellTest) throws ParseException {
         wellTestService.createWellTest(wellTest);
      List<WellTest> wt = wellTestService.getWellTestByWellTestID(wellTestID);
      return wt;
     }

The only way I can get this to pick up the params is by concatenating the the request params onto the URL, which we all know is wrong. But after spending days identifying the issue, I'll take any hack that will work. The testing tools I used really didn't help me get ready for this problem, because POSTing a JSON payload is different from form variables.

So that is the issue, and if there is anyone out there who is experiencing this anomaly, my hope is an expert will reply here with a complete and demonstrated solution.

Question is: How to use Jackson to parse and bind the JSON payload on a POST (a PUT will work the same regarding W3C recommendations) - I am using Glassfish 4.0 if that makes a difference.

I have a gotcha for people to consider when using these "free" examples that many of us start with. The gotcha is always, and I mean always, run any demonstration software offered that uses services. I did after I couldn't make this work and found that the updates were actually GET's with a variable pattern that I found strange, but is was concatenated to the URL before it was requested. I have spent way too much time trying to learn this to implement it with knowledge, but I am just not able to do it in time.

Thanks to all that answer regarding this post. You have my gratitude.

Curtis Fisher

I have received an answer that makes for more questions rather than leading me closer to the answer. I appreciate it, but I am unable to implement anything with just that snippet of knowledge.

Here is how the beans are being loaded...

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org /2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans     
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.aspecteg.springservice.controller" />
<mvc:annotation-driven />
</beans>

Well I made the mistake of editing Ms. Davis's post and the questions I asked went into the ether called Peer Review. No doubt I'll be chastized for some infraction.

First, I wanted to thank you and Mr. Sumner for answering. My next questions are because I noticed Ms. Davis's example built upon Mr. Sumner's suggestion, and it appears that the Model Object in both JSON and Jave (WellTest) is auto populated by the

MyObject res = myObjectRepository.save(myObject);

line of code. Now this looks pretty cool. However I have a few more questions...

What kind of object is the...

 @Autowired
private MyObjectRepository myObjectRepository;

and how is it "wired" in?

@ResponseStatus(HttpStatus.CREATED)

What is the @ResponseStatus above responsible for?

What is the Logger you are using, Log4J?

Given the code below, do either of you have any corrections?

@RestController
@RequestMapping("/service/welltest")
public class WellTestController {

private static Logger LOG = LoggerFactory.getLogger(MyObjectsController.class);

@Autowired
private MyObjectRepository myObjectRepository; //NEED MORE INFO ON THIS - Curtis

@RequestMapping(value = "/update/{wellTestID}",  method = RequestMethod.PUT,headers="Accept=application/json",
                     consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public WellTest createUserPayment(@RequestBody WellTest wellTest) {
    WellTest wt = myObjectRepository.save(wellTest);
    LOG.info("the result of the save: "+wt.toString());
    return res;
}
}

Finally, since I have my beans loaded via an xml file and not with annotated classes, is there any Jackson beans or objects that need to be loaded in the definition? Or any other configuration specifics? As soon as I hear back from either of you regarding my questions I will implement this. If I don't hear from either of you for awhile, I'll try a variation of Mr. Sumner's solution because I don't know how to implement the @autoWired portion of Ms. Davis. I'm ready though!

Thank you both...

Curtis Fisher


Solution

  • I don't fully understand your problem because you haven't provided a clear indication of what the problem actually is. That being said, the following should work just fine:

    @RestController
    class FooController {
      @RequestMapping(
        value = "/some/endpoint",
        method = RequestMethod.POST,
        consumes = MediaType.APPLICATION_JSON_VALUE,
        produces = MediaType.APPLICATION_JSON_VALUE
      )
      public MyResponseObject someEndpoint(@RequestBody MyRequestObject requestBody)
      {
        MyResponseObject responseObject = new MyResponseObject();
        responseObject.setSomeProperty("foo");
        responseObject.setAnotherProperty(responseObject.getSomeProperty("bar"));
    
        return responseObject;
      }
    }