RequestBody + JSON - java

I have problem with convert JSON to Java class.
Controller
#RequestMapping(value = "/{username}/add", method = POST)
public void add(#RequestBody NoteModel note) {
System.out.println(note.getTitle());
}
JSON
{
title : "Title",
text : "Text"
}
NoteModel
public class NoteModel {
private String title;
private String text;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
So, when I send json to the controller, Controller see same url, but can't deserialize JSON to Java (I think). Because, when I try, to send JSON - { title : "Title" }, and controller wait argument - #RequestBody String note, it can easily display it.
I'm try to do, what was in https://gerrydevstory.com/2013/08/14/posting-json-to-spring-mvc-controller/ and include adapter in servlet.xml, but was the same effect.
AJAX
$.ajax({
type : "POST",
contentType : "application/json; charset=utf-8",
url : window.location.pathname,
data : JSON.stringify({
title : $("#titleId").val(),
text : $("#textId").val()
}),
success: function () {
$("#titleId").val("");
$("#textId").val("");
}
})

Add #RequestMapping(value = "/{username}/add", method = POST, produces = "application/json")

Make sure that you have added content-type to "application/json" in header of your request.

how to catch the problem:
Send the String to your controller and try to create your object. Put breakpoint to objectMapper.readValue() and check what is the exactly problem;
#RequestMapping(value = "/{username}/add", method = POST)
public void add(#RequestBody String note) {
ObjectMapper objectMapper = new ObjectMapper();
NoteModel noteModel = objectMapper.readValue(result, NoteModel.class);
}
I think that there is some conflict between default ObjectMapper and JSON mapper logic.

Related

Java - #ResponseBody String - error 200

I'm trying to get a string response from my controller but I get the below error:
SyntaxError: Unexpected end of JSON input(…) "Error 200"
When I change the response to a boolean or a different type, it's working ok. The problem is when I try to return a string.
js code:
$.ajax({
method: "POST",
url: "./signup",
data: _data,
dataType: "json",
contentType : "application/json;charset=UTF-8",
success : function(data) {
console.log(data)
},
error : function(qXHR, textStatus, errorThrown){
console.log(errorThrown, "Error " + qXHR.status);
}
});
controller code:
#RequestMapping(value = "/signup", method = RequestMethod.POST, produces = {"text/plain", "application/*"})
public #ResponseBody String signup(#RequestBody UserSignup details) {
//...
return message;
}
any idea how can I solve this problem? I have tried a few things but nothing work. I think the response format is wrong as what the code expects.
Edit
I have changed my code(removed produces) but I still getting the same error:
SyntaxError: Unexpected end of JSON input(…) "Error 200"
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public #ResponseBody String signup(#RequestBody UserSignup details) {
message = "ok";
}
return message;
}
Your method is wrong. You are saying to produce produces = {"text/plain", "application/*"} But you are also adding the #ResponseBody which will generate JSON format response.
I would suggest you remove the attribute produces. And verify the string you are returning is well formed
Try to wrap your response in ResponseEntity class
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public #ResponseBody ResponseEntity<String> signup(#RequestBody UserSignup details) {
message = "ok";
return new ResponseEntity<>(message, HttpStatus.OK);
}
Also double check data that you are sending to server, maybe this is the problem, can you show us _data value?
As I don't have problem when the response is different stuff as a String I have solved the problem creating my own object. So below is the code:
public class Response<T> {
private T message;
private Exception ex;
public Exception getEx() {
return ex;
}
public void setEx(Exception ex) {
this.ex = ex;
}
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
}
#Controller
public class MyControllerController {
private Response<String> _response;
private String message;
public MyController() { _response = new Response<>(); }
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public #ResponseBody Response<String> signup(#RequestBody UserSignup details) {
try{
message = "";
// code...
_response.setMessage(message);
return _response;
}catch (Exception ex){
_response.setEx(ex);
return _response;
}
}
}
response example in the browser:
Object {message: "", ex: null}

Parameters of POST request to Java server from Angular client

I've got Angular app and Java server.
I need to send POST request with JSON object consisting of string array and string field.
I'm using Angularjs $resource and Java javax.ws.rs.
My latest try as follows:
Client:
var messages = $resource('resources/messages/getmessages', {}, {
update: { method: 'POST', url: 'resources/messages/updatemessages' }
});
//...
var _args = { 'msgIdList': ['1', '2', '3'],
'action': 'makeSmth' };
return messages.update(_args).$promise.then(
function (data) {
//...
},
function (error) {
//...
}
)
Server:
#POST
#Path("updatemessages")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON +"; charset=UTF-8")
public Response updateMessages( #FormParam("msgIdList") List<String> msgIdList,
#DefaultValue("") #FormParam("action") String action,
#CookieParam("rgsid") String c_sid,
#Context HttpServletRequest httpservletreq) {
//...
}
The problem is that I've got 415 Unsupported Media Type error, and don't know what to do next. I've tried lots of things, but may be I was wrong from the start, and I can't pass parameters this way?
Any help would be appreciated, thanks!
you can try this in your angular, maybe it can help.
var sendPost = $http({
method: "post",
url:"JAVA_SERVER_SERVICE_URL",
data: {
msgIdList: 'your_value',
action: 'your_value'
},
headers: { 'Content-Type': 'application/json' }
});
So, eventually I made a wrapper class, so now it looks this way:
#XmlRootElement
private static class RequestWrapper {
#XmlElement
private ArrayList<String> msgIdList;
#XmlElement
private String action;
public ArrayList<String> getMsgIdList() {
return msgIdList;
}
public void setMsgIdList(ArrayList<String> msgIdList) {
this.msgIdList = msgIdList;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public RequestWrapper() {
}
}
#POST
#Path("updatemessages")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON +"; charset=UTF-8")
public Response updateMessages( RequestWrapper requestData,
#CookieParam("rgsid") String c_sid,
#Context HttpServletRequest httpservletreq) {
//...}
Angular part stays unchanged.
I'm not really sure, if this the right way to go (class description and so on), but it works.

Documenting a wrapped REST response using swagger UI

I have a WidgetDto that I have annotated with swagger UI annotations. The final response wraps a list of WidgetDtos with a layer of metadata (per page 21 of this RESTful best practices document). For example:
{
"data" : [
{
"id" : 1234,
"prop1" : "val1"
...
},
{
"id" : 5678,
"prop1" : "val2"
...
},
...
]
}
My java code looks like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
List<WidgetDto> widgets;
...
Map<String, Object> responseBody = new HashMap<>();
responseBody.put("data", widgets);
return Response.ok(responseBody).build();
}
I'd like to reuse this pattern on multiple resources, and I don't want to create list DTOs for every response type. Is there an elegant way to use swagger to document these types of response bodies?
Your metadata is not a part of your resource but it's a part of your resource's representation.
In my case, responses types are 'application/hal+json' and 'application/json', each of them use a different wrapper with different metadatas.
To solve this problem, I created an extern document to explain these two wrappers and for each of them, how a single resource and a list of resources are represented with metadata.
I think my choice is correct because I separate the resource of its representations (per page 7 'Manipulation of Resources Through Representations' of this RESTful best practices document)
In your case, you returns a list of WidgetDtos, the layer of metadata is a part of the representation of your resource.
However, you can use a generic class like Resource and Resources used by spring-hateoas :
public class Resources<T> implements Iterable<T> {
private final Collection<T> content;
Resources(Iterable<T> content) {
this.content = new ArrayList<T>();
for (T element : content) {
this.content.add(element);
}
}
}
And use it like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response getWidgets() {
List<WidgetDto> widgets;
...
return Response.ok(new Resources<WidgetDto>(widgets)).build();
}
I faced a similar problem a few months ago when I was developing a project for school. The solution is to create an envelope and always return it. The envelope will contain a feild "data" which is a generic; so you will be able to bind it to any data type.
Note that even though I used it I later on read that it should be used scarecly (I think your case is a good example of usage) but technically an Exception object should be thrown if the request failed.
Anyway this is my Response class which I used to return all my responses:
public class Response <AnyData> {
private static final String SUCCESS = "success";
private static final String FAILURE = "failure";
private String status;
private AnyData data;
private String error;
private Response(String status, AnyData data, String error) {
this.status = status;
this.data = data;
this.error = error;;
}
private Response(String status, AnyData data) {
this(status, data,"");
}
private Response(String status, String error) {
this(status, null, error);
}
public static <AnyData> Response<AnyData> success(AnyData data) {
return new Response<AnyData>(SUCCESS, data);
}
public static <AnyData> Response<AnyData> failure(String error) {
return new Response<AnyData>(FAILURE, error);
}
public static <AnyData> Response<AnyData> unimplemented() {
return new Response<AnyData>(FAILURE, "Missing implementation in the backend.");
}
public static <AnyData> Response<AnyData> failureUserNotFound() {
return Response.failure("User not found!");
}
public static <AnyData> Response<AnyData> failureBusinessNotFound() {
return Response.failure("Business not found!");
}
// Removed getters and setters for simplicity.
}
After this is set we will just create the responses right from the Comtroller. I changed it a bit to make it work with the sample is should be legible enough. Note that I have static methods for my responses: 'success()', 'error()'...
#RestController
#Api(tags={"Widgets"})
public class WidgetController {
#RequestMapping(value="/api/widgets", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get all widgets.")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Returns the list of widgets.")
})
public Response<List<WidgetDto>> getWidgets() {
List<WidgetDto> widgets = new LinkedList<>();
widgets.add(new WidgetDto(1234, "val1"));
widgets.add(new WidgetDto(5678, "val2"));
return Response.success(widgets);
}
}
And here is a sample of the response body:
Hope this helps.
You can define the responseContainer attribute in the #ApiOperation annotation.
The value List will wrap your WidgetDto in a container.
#ApiOperation(
value = "Get all widgets.",
response = WidgetDto.class,
responseContainer = "List"
)

How to use javax.validation and JSON request in Spring 4 MVC?

I'm developing a Web App using Spring 4 MVC. I want to know If I can validate JSON request objects with javax.validation API. For example I have this chunk of my entity code:
...
#JsonProperty("cheFecha")
#NotNull
#Column(name = "che_fecha")
#Temporal(TemporalType.DATE)
#DateTimeFormat(style = "M-")
private Date SsiCheque.cheFecha;
#JsonProperty("cheMonto")
#NotNull
#JsonSerialize(using = CurrencySerializer.class)
#Column(name = "che_monto", precision = 10, scale = 2)
private BigDecimal SsiCheque.cheMonto;
...
I have the controller code:
#RequestMapping(value = "/addCheck", method = RequestMethod.POST)
public #ResponseBody SsiCheque addChecks(#Valid SsiCheque ssiCheque, BindingResult result) {
//ssiCheque.persist();
System.out.println("add" + result.getErrorCount());// Zero when there are errors
return ssiCheque;
}
And finally I have the jQuery code:
var formData = $("#formAddChecks :input").serializeArray();
$.ajax({
type: "POST",
url: "addCheck",
data: formData,
beforeSend: function ( xhr ) {
console.log("before Send");
},
error: function (request, status, error) {
console.log('Error ' + "\n" + status + "\n" + error);
},
success: function(data) {
console.log(data);
}
});
The JSON object is arriving correctly to the controller but I want to validate the JSON with the entity javax.annotations API. What I have seen is only using custom validators and "rewrite" the validation code.
Is this the only way to validate JSON?
Thanks in advance!
UPDATE 1
I followed the #James Massey suggestions and my code looks like this right now:
Controller
#RequestMapping(value = "/addCheck", method = RequestMethod.POST)
#ResponseBody
public SsiCheque addChecks(#Valid #RequestBody SsiCheque ssiCheque, BindingResult result) {
//ssiCheque.persist();
System.out.println("agregar " + result.getErrorCount());
return ssiCheque;
}
Javascript file
var ssiCheque = {
cheNumero : $("#formAddChecks cheNumero").val(),
cheRecepto : $("#formAddChecks cheReceptor").val(),
cheMonto : $("#formAddChecks cheMonto").val(),
cheFecha : $("#formAddChecks cheFecha").val(),
cheConcepto : $("#formAddChecks cheConcepto").val()
};
$.ajax({
type: "POST",
contentType: "application/json",
url: "addCheck",
data: ssiCheque,
dataType: "json",
beforeSend: function ( xhr ) {
console.log("before Send");
},
error: function (request, status, error) {
console.log('Error ' /*+ request.responseText*/ + "\n" + status + "\n" + error);
},
success: function(data) {
console.log(data);
}
});
But I'm getting an 400 Error (Incorrect request) when I submit the form and execute the Ajax function. I have faced this error before when the json object format and the controller specs were incompatible, but in this time I don't know why can be the error.
Thanks again!
I have solved my validations in another way. Suppose I have and Agent Object:
public class Agent {
public int userID;
public String name;
public boolean isVoiceRecorded;
public boolean isScreenRecorded;
public boolean isOnCall;
}
I would like to validate :
(1) userID>0
(2) name is mandatory
(3) isVoiceRecorded and isScreenRecorded can be true only if isOnCall is true.
In order to do so I need to add dependency :
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
Now look how Agents class looks like:
#NoArgsConstructor
#ToString
#EqualsAndHashCode(of = "userID")
#CheckBools
public class Agent {
#Min(0)
public int userID;
#NotNull(message = "Name cannot be null")
public String name;
public boolean isVoiceRecorded;
public boolean isScreenRecorded;
public boolean isOnCall;
public LocalDateTime startEventDateTime;
}
(1) #Min(0) - solves userID>0
(2) #NotNull(message = "Name cannot be null") - solves name is mandatory, and you have example how to specify error message
(3) #CheckBools annotation defined by me, at the class level which checks isVoiceRecorded and isScreenRecorded can be true only if isOnCall is true.
#Documented
#Constraint(validatedBy = MyConstraintValidator.class)
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
public #interface CheckBools {
String message() default "'isVoiceRecorded' or 'isScreenRecorded' can be true only if you are on call";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
In the following class you define the rule
public class MyConstraintValidator implements ConstraintValidator<CheckBools, Agent> {
#Override
public void initialize(CheckBools constraintAnnotation) {
}
#Override
public boolean isValid(Agent value, ConstraintValidatorContext context) {
if (!value.isOnCall && (value.isVoiceRecorded || value.isScreenRecorded))
return false;
else return true;
}
}
At the controller level :
#RestController
#RequestMapping("Myteamview")
public class MyteamviewController {
#Autowired
AgentInfo agentInfo;
#RequestMapping(path = "agents", method = RequestMethod.POST)
public ResponseEntity<Boolean> addOrUpdateAgent(#Valid #RequestBody Agent agent) {
ResponseEntity<Boolean> responseEntity = new ResponseEntity<>(agentInfo.addAgent(agent),HttpStatus.OK);
return responseEntity;
}
}
Note: The important is that you specify #Valid before #RequestBody Agent
There appear to be a few problems here:
Your object structure seems weird. Why are your fields referencing an object type? private Date SsiCheque.cheFecha seems to be a totally non-sensical field.
You generally design your UI to send through a JSON object that can be mapped directly into your Java object. So if your object looked like this:
public class Example {
#NotNull
#Digits(fraction = 2, integer = 10)
private Integer foo;
#NotEmpty
private String bar;
#NotEmpty
private String[] baz;
}
Then your JSON structure would be something like this:
{
"example": {
"foo": 1,
"bar": "Pineapple",
"baz": [
"This is a string",
"So is this"
]
}
}
Which can be used by Jackson to map straight into your object.
You would then write your controller method like this assuming that you had the Jackson JAR included in your project classpath:
#RequestMapping(value = "/example", method = RequestMethod.POST)
#ResponseBody
public Example(#Valid #RequestBody Example example, BindingResult result) {
if(result.hasErrors()){
//A validation has failed, return an error response to the UI
} else {
exampleService.createOrUpdate(example);
return example;
}
}
The important part is that your object is the request body and you use the #RequestBody annotation, as Jackson uses this as a signal to construct your object using the JSON present in your HTTP Request Body. The only downside to this method is that you may have to construct your request JSON programmatically. This is trivial to do with JavaScript however.
(I'm going to assume some sensible input id defaults here, and that you are familiar with the jQuery DOM manipulation/selection syntax)
var bazArray = [];
$.forEach($("#bazContainer"), function (baz, i){
bazArray.push(baz);
});
var example = {
foo: $("#fooInput").val(),
bar: $("#barInput").val(),
baz: bazArray
};
You pass in your example object to your request in the data field, and if you specify that it is of type application/json then jQuery will automatically call JSON.stringify on your example object.
Hopefully this all makes sense.
SOLUTION (Updated by questioner: Jessai)
I checked this question: Spring MVC 400 Bad Request Ajax.
In summary what I did:
Create an object to be parsed with JSON.stringify and send it to the controller.
In the controller I set the method with #ResponseBody and #RequestBody as #James Massey said.
In the entity I added #JSONProperty (I had these already) and #JSONIgnore (I added to cheId field) annotations to the fields.
Javascript:
var ssiCheque = {
cheNumero : $("#formAddChecks #cheNumero").val(),
cheRecepto : $("#formAddChecks #cheReceptor").val(),
cheMonto : $("#formAddChecks #cheMonto").val(),
cheFecha : $("#formAddChecks #cheFecha").val(),
cheConcepto : $("#formAddChecks #cheConcepto").val()
};
$.ajax({
type: "POST",
contentType: "application/json",
url: "addCheck",
data: JSON.stringify(ssiCheque),
dataType: "json",
beforeSend: function ( xhr ) {
console.log("before Send");
},
error: function (request, status, error) {
console.log('Error ' /*+ request.responseText*/ + "\n" + status + "\n" + error);
},
success: function(data) {
console.log(data);
}
});
Controller
#RequestMapping(value = "/addCheck", method = RequestMethod.POST)
#ResponseBody
public SsiCheque addChecks(#Valid #RequestBody SsiCheque ssiCheque, BindingResult result) {
//ssiCheque.persist();
System.out.println("agregar " + result.getErrorCount());
return ssiCheque;
}
Thanks!

spring generic json response

I am using Spring MVC and returning JSON as response. I would like to create a generic JSON response where I can put in any TYPE and want the response to look like this
{
status : "success",
data : {
"accounts" : [
{ "id" : 1, "title" : "saving", "sortcode" : "121212" },
{ "id" : 2, "title" : "current", "sortcode" : "445566" },
]
}
}
So I created a Response<T> object
public class Response<T> {
private String status;
private String message;
T data;
...
...
}
Is this the correct way of doing this, or is there a better way?.
How do you use this Response object in Spring controller to return an empty response object and/or a populated response object.
Thanks in advance GM
UPDATE:
In order to get the similar JSON output as the one described, i.e. with "accounts" key in JSON, I had to use Response<Map<String, List<Account>>> the following in the controller:
#RequestMapping(value = {"/accounts"}, method = RequestMethod.POST, produces = "application/json", headers = "Accept=application/json")
#ResponseBody
public Response<Map<String, List<Account>>> findAccounts(#RequestBody AccountsSearchRequest request) {
//
// empty accounts list
//
List<Account> accountsList = new ArrayList<Account>();
//
// response will hold a MAP with key="accounts" value="List<Account>
//
Response<Map<String, List<Account>>> response = ResponseUtil.createResponseWithData("accounts", accountsList);
try {
accountsList = searchService.findAccounts(request);
response = ResponseUtil.createResponseWithData("accounts", accountsList);
response.setStatus("success");
response.setMessage("Number of accounts ("+accounts.size()+")");
} catch (Exception e) {
response.setStatus("error");
response.setMessage("System error " + e.getMessage());
response.setData(null);
}
return response;
}
Is this the right way of doing this? i.e. in order to get the "accounts" key in JSON output?
While your example JSON is not valid (status and data are not enclosed in quotations), this approach will work.
You will want to ensure that you have the Jackson jars on your classpath, and Spring will take care of the rest.
To get this to work, I would create a constructor for your response class that looks something like this:
public class Response<T> {
private String status;
private String message;
private T data;
public Response(String status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
//...getter methods here
}
And then in your Spring controller, you just return this object from your method that is mapped with #RequestMapping
#Controller
public class MyController {
#RequestMapping(value="/mypath", produces="application/json")
public Response<SomeObject> myPathMethod() {
return new Response<SomeObject>("200", "success!", new SomeObject());
}
}

Categories