For some reason java can't map DTO with requestBody and all values are default ones, as for request it works, with payload for ex. "{"productId":1,"commitment":6,"returnMonths":"2"}"
DTO
#Data
public class Request {
private int productId;
private int commitment;
private String returnMonths;
// contructers
}
Controller :
#PostMapping(value = "/calculate", consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String calculatePrice(#RequestBody Request request) {
productService.calculatePrice(request);
return "Success";
}
front request:
submit: async function() {
let request = {
productId: this.productSelected,
commitment: this.optionSelected,
returnMonths: this.input
};
let data = await getCalculation(request);
console.log(data);
}
DTO maps as:
productId : 0
commitment : 0
returnMonths : null
Tried an exact copy of your code and it worked when tested with Postman. This makes me think it's either something to do with the FE or maybe some issue in the service. I'd check if the Frontend really sends the data.
Try to annotation Request class with #AllArgsConstructor like:
#AllArgsConstructor
#Data
public class Request {
private int productId;
private int commitment;
private String returnMonths;
}
If your request body contains properties that is date such as LocalDateTime, make sure to format it in your DTO using #JsonFormat(pattern="") respecting the input value.
Related
I have a simple controller:
#RequestMapping(method = { RequestMethod.POST })
public ResponseEntity<MyResponse> print(#RequestBody final RequestModel request) throw ApiException {
return null;
}
And in my RequestModel:
class RequestModel {
private String name;
private CustomData data;
}
CustomData:
class CustomData {
private String data;
}
When I make POST request without the "data" field, it works. But if I add the "data" field, I'm getting a 400, The request sent by the client was syntatically incorrect.
O dont know If you wrote all the code, but tou should implements serializable and write setters and getters.
But, answering your question, you should annotate your fields with #JsonProperty to specify the required flag.
Your posted JSON should be something like this :
{
"name":"Luke",
"data": {
"data":"I am your father"
}
}
OBS: if you are using Postman, please set the header : key: Content-Type, value: application/json
You should specify an endpoint:
Example :
#PostMapping("/data")
Instead of
#RequestMapping(method = { RequestMethod.POST })
If you are using default port, try again the post to :
http://localhost:8080/data
OBS: RequestModel and CustomerData must have getters and setters.
I'm trying to create a Java object by deserialising a JSON message in Spring Boot. I have a class as such:
public class Status implements Serializable {
private int uptime;
}
Then I have a Rest Controller as such:
#RestController
public class StatusReceiver {
#RequestMapping(value = "/poststatus", method = RequestMethod.POST)
public #ResponseBody Status storeStatus(#RequestBody Status statusMessage) {
System.out.println("Uptime: " + statusMessage.getUptime());
return statusMessage;
}
}
I definitely don't understand a fair bit of what I'm doing here. I want the controller to be creating a Status object when it receives the message and have it store it in the database.
Output of this code is:
Uptime: 0
The message it's receiving is:
{
"Status": [
{
"uptime": 12345.0
}
]
}
I'm obviously missing something. How can I have Spring convert the JSON object into the Status java object? Also where is the return from the method meant to be going and doing?
The request body JSON is not matching the Status object structure. Either your body should be
{uptime: 12345.0}
or the Status class should be
class Status {
List<Map<String, Long>> status = new ArrayList<>();
}
Instead of having a List of Map, you can define a new class and have a list of this new class
class StatusInfo{
long uptime;
}
class Status {
List<StatusInfo> status = new ArrayList<>();
}
Suppose i have a page that lists the objects on a table and i need to put a form to filter the table. The filter is sent as an Ajax GET to an URL like that: http://foo.com/system/controller/action?page=1&prop1=x&prop2=y&prop3=z
And instead of having lots of parameters on my Controller like:
#RequestMapping(value = "/action")
public #ResponseBody List<MyObject> myAction(
#RequestParam(value = "page", required = false) int page,
#RequestParam(value = "prop1", required = false) String prop1,
#RequestParam(value = "prop2", required = false) String prop2,
#RequestParam(value = "prop3", required = false) String prop3) { ... }
And supposing i have MyObject as:
public class MyObject {
private String prop1;
private String prop2;
private String prop3;
//Getters and setters
...
}
I wanna do something like:
#RequestMapping(value = "/action")
public #ResponseBody List<MyObject> myAction(
#RequestParam(value = "page", required = false) int page,
#RequestParam(value = "myObject", required = false) MyObject myObject,) { ... }
Is it possible?
How can i do that?
You can absolutely do that, just remove the #RequestParam annotation, Spring will cleanly bind your request parameters to your class instance:
public #ResponseBody List<MyObject> myAction(
#RequestParam(value = "page", required = false) int page,
MyObject myObject)
I will add some short example from me.
The DTO class:
public class SearchDTO {
private Long id[];
public Long[] getId() {
return id;
}
public void setId(Long[] id) {
this.id = id;
}
// reflection toString from apache commons
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
Request mapping inside controller class:
#RequestMapping(value="/handle", method=RequestMethod.GET)
#ResponseBody
public String handleRequest(SearchDTO search) {
LOG.info("criteria: {}", search);
return "OK";
}
Query:
http://localhost:8080/app/handle?id=353,234
Result:
[http-apr-8080-exec-7] INFO c.g.g.r.f.w.ExampleController.handleRequest:59 - criteria: SearchDTO[id={353,234}]
I hope it helps :)
UPDATE / KOTLIN
Because currently I'm working a lot of with Kotlin if someone wants to define similar DTO the class in Kotlin should have the following form:
class SearchDTO {
var id: Array<Long>? = arrayOf()
override fun toString(): String {
// to string implementation
}
}
With the data class like this one:
data class SearchDTO(var id: Array<Long> = arrayOf())
the Spring (tested in Boot) returns the following error for request mentioned in answer:
"Failed to convert value of type 'java.lang.String[]' to required type
'java.lang.Long[]'; nested exception is
java.lang.NumberFormatException: For input string: \"353,234\""
The data class will work only for the following request params form:
http://localhost:8080/handle?id=353&id=234
Be aware of this!
Since the question on how to set fields mandatory pops up under each post, I wrote a small example on how to set fields as required:
public class ExampleDTO {
#NotNull
private String mandatoryParam;
private String optionalParam;
#DateTimeFormat(iso = ISO.DATE) //accept Dates only in YYYY-MM-DD
#NotNull
private LocalDate testDate;
public String getMandatoryParam() {
return mandatoryParam;
}
public void setMandatoryParam(String mandatoryParam) {
this.mandatoryParam = mandatoryParam;
}
public String getOptionalParam() {
return optionalParam;
}
public void setOptionalParam(String optionalParam) {
this.optionalParam = optionalParam;
}
public LocalDate getTestDate() {
return testDate;
}
public void setTestDate(LocalDate testDate) {
this.testDate = testDate;
}
}
//Add this to your rest controller class
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String testComplexObject (#Valid ExampleDTO e){
System.out.println(e.getMandatoryParam() + " " + e.getTestDate());
return "Does this work?";
}
I have a very similar problem. Actually the problem is deeper as I thought. I am using jquery $.post which uses Content-Type:application/x-www-form-urlencoded; charset=UTF-8 as default. Unfortunately I based my system on that and when I needed a complex object as a #RequestParam I couldn't just make it happen.
In my case I am trying to send user preferences with something like;
$.post("/updatePreferences",
{id: 'pr', preferences: p},
function (response) {
...
On client side the actual raw data sent to the server is;
...
id=pr&preferences%5BuserId%5D=1005012365&preferences%5Baudio%5D=false&preferences%5Btooltip%5D=true&preferences%5Blanguage%5D=en
...
parsed as;
id:pr
preferences[userId]:1005012365
preferences[audio]:false
preferences[tooltip]:true
preferences[language]:en
and the server side is;
#RequestMapping(value = "/updatePreferences")
public
#ResponseBody
Object updatePreferences(#RequestParam("id") String id, #RequestParam("preferences") UserPreferences preferences) {
...
return someService.call(preferences);
...
}
I tried #ModelAttribute, added setter/getters, constructors with all possibilities to UserPreferences but no chance as it recognized the sent data as 5 parameters but in fact the mapped method has only 2 parameters. I also tried Biju's solution however what happens is that, spring creates an UserPreferences object with default constructor and doesn't fill in the data.
I solved the problem by sending JSon string of the preferences from the client side and handle it as if it is a String on the server side;
client:
$.post("/updatePreferences",
{id: 'pr', preferences: JSON.stringify(p)},
function (response) {
...
server:
#RequestMapping(value = "/updatePreferences")
public
#ResponseBody
Object updatePreferences(#RequestParam("id") String id, #RequestParam("preferences") String preferencesJSon) {
String ret = null;
ObjectMapper mapper = new ObjectMapper();
try {
UserPreferences userPreferences = mapper.readValue(preferencesJSon, UserPreferences.class);
return someService.call(userPreferences);
} catch (IOException e) {
e.printStackTrace();
}
}
to brief, I did the conversion manually inside the REST method. In my opinion the reason why spring doesn't recognize the sent data is the content-type.
While answers that refer to #ModelAttribute, #RequestParam, #PathParam and the likes are valid, there is a small gotcha I ran into. The resulting method parameter is a proxy that Spring wraps around your DTO. So, if you attempt to use it in a context that requires your own custom type, you may get some unexpected results.
The following will not work:
#GetMapping(produces = APPLICATION_JSON_VALUE)
public ResponseEntity<CustomDto> request(#ModelAttribute CustomDto dto) {
return ResponseEntity.ok(dto);
}
In my case, attempting to use it in Jackson binding resulted in a com.fasterxml.jackson.databind.exc.InvalidDefinitionException.
You will need to create a new object from the dto.
Yes, You can do it in a simple way. See below code of lines.
URL - http://localhost:8080/get/request/multiple/param/by/map?name='abc' & id='123'
#GetMapping(path = "/get/request/header/by/map")
public ResponseEntity<String> getRequestParamInMap(#RequestParam Map<String,String> map){
// Do your business here
return new ResponseEntity<String>(map.toString(),HttpStatus.OK);
}
Accepted answer works like a charm but if the object has a list of objects it won't work as expected so here is my solution after some digging.
Following this thread advice, here is how I've done.
Frontend: stringify your object than encode it in base64 for submission.
Backend: decode base64 string then convert the string json into desired object.
It isn't the best for debugging your API with postman but it is working as expected for me.
Original object: { page: 1, size: 5, filters: [{ field: "id", value: 1, comparison: "EQ" }
Encoded object: eyJwYWdlIjoxLCJzaXplIjo1LCJmaWx0ZXJzIjpbeyJmaWVsZCI6ImlkUGFyZW50IiwiY29tcGFyaXNvbiI6Ik5VTEwifV19
#GetMapping
fun list(#RequestParam search: String?): ResponseEntity<ListDTO> {
val filter: SearchFilterDTO = decodeSearchFieldDTO(search)
...
}
private fun decodeSearchFieldDTO(search: String?): SearchFilterDTO {
if (search.isNullOrEmpty()) return SearchFilterDTO()
return Gson().fromJson(String(Base64.getDecoder().decode(search)), SearchFilterDTO::class.java)
}
And here the SearchFilterDTO and FilterDTO
class SearchFilterDTO(
var currentPage: Int = 1,
var pageSize: Int = 10,
var sort: Sort? = null,
var column: String? = null,
var filters: List<FilterDTO> = ArrayList<FilterDTO>(),
var paged: Boolean = true
)
class FilterDTO(
var field: String,
var value: Any,
var comparison: Comparison
)
I'm working on adapting angular-ui-tree to persist the tree structure in a one-to-many entity model. The piece I am missing is how to have Spring/Jackson interpret the following POST body
data: "{
"id":1.7976931348623157e+308,
"name":"New Category -- Need Name",
"parent":6,
"type":"create"
}"
that is serialized and delivered using a $http service method in Angular:
$http.post("/rest/category",
{data: angular.toJson(updateData)}
)
.success(function(data, status, headers, config){
return [data, status];
})
.error(function(data, status, headers, config){
return [data, status];
})
Right now, I only get error 405s with the address, with the return value stating that
Request method 'POST' not supported
So, I modified the controller below to take in a DummyCategory object from the RequestBody:
#RequestMapping(value = "/rest/category", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public List<Category> createCategory(#RequestBody DummyCategory data ){
log.info("Start creating category " + data);
}
which is this entity:
#JsonDeserialize(using = DummyCategoryDeserializer.class)
public class DummyCategory {
private String type;
private String name;
private Long id;
private int parent;
//getters and setters
}
and corresponding DummyCategoryDeserializer class:
public class DummyCategoryDeserializer extends JsonDeserializer<DummyCategory> {
#Override
public DummyCategory deserialize(JsonParser jp, DeserializationContext ctx) throws IOException {
String tempData = jp.getText();
DummyCategory dummyCategory = new DummyCategory();
return dummyCategory;
}
}
Since I am very new at this, my goal is to have a debugger breakpoint set at the deserializer method and work my way through the properties. However, after making these custom modifications, the $http service is still returning 405s on execution, and the breakpoints are not being hit.
I know that the server is functional, because all GET requests are returning expected values.
If you want to see the actual source code for this question, it is available on the category-taxonomy branch. The new deserializer classes are within /models and /utils
I'm using AndroidAnnotations to build a Rest for an Android Application.
On the Serverside im using PHP, which send a json looking like :
{"tag":"register","success":0,"error":2,"msg":"User already existed","body":[]}
I have two POJOS :
User.java:
public class User implements Serializable {
private String name;
private String email;
private String password;
//getter and setter Methods
}
Response.java:
public class RegistrationResponse implements Serializable {
private String tag;
private int success;
private int error;
private String msg;
private String body;
//getter and setter Methods
}
Rest Client:
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
Response sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
Activity.java:
//User and Response are POJOs
Response result = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
But i got an Null Pointer Exception error on Activity.java. But if i change the return value of "sendUserRegistration" function to String all work. So my "Response" POJO seems not to be converted from AndroidAnnotations.
How can i convert the Rest Response to my "Response"-POJO using AndroidAnnotations?
You don't need to return the entire response object per rest call, just set the response to your custom object. Or you can also return a JsonObject also and use gson to convert it later on.
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
User sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
then just simply call
User newUser = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
AA relies on Spring Android RestTemplate to make the rest call. And in order to build requests and handle responses this lib uses converters. And to know which converter the RestTemplate should use, it checks the content-type response header.
As MappingJacksonHttpMessageConverter and GsonHttpMessageConverter handles only http response with content-type=application/json and your result is converted to string, I'm pretty sure you forgot to set this header in your php server. So it send the default one (ie: text/plain) which is only handle by StringHttpMessageConverter.
Also, the body field is an object in your json example, but in your POJO you declared it as a String. So parsing will fail on this point.