Spring #RequestBody Bad Request - java

I am using POSTMAN to send request's.
Configured all right there:
Content-Type application/json
Request Type POST
and in Body I have the following:
{
"token":"EAACEdEose0cBAFLc4blCYmmetEMBEZCiQQZAuvz6DlxFt0yPZCksZBWv09B71aZCeDH9zOPyzM44GRl8WA56uFZBmOiUMmSlk3USfOwRdwmXDnhlPArttzjjLzUXaTReHzHZC7ZCcFzZADwGBLRUHvTb17nagRDLpZBysdxZBxuJuojlgZDZD"
}
I POST this to a controller and get a 400 BAD Request Error:
{
"timestamp": 1475061564742,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "Could not read document: Can not construct instance of at.wastun.controller.WTUserController$RegisterBody: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)\n at [Source: java.io.PushbackInputStream#a646ac6; line: 2, column: 2]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of at.wastun.controller.WTUserController$RegisterBody: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)\n at [Source: java.io.PushbackInputStream#a646ac6; line: 2, column: 2]",
"path": "/users/register"
}
The class and the Controller looks like:
#Controller
#RequestMapping("/users")
#ResponseBody
public class WTUserController {
private class RegisterBody{
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public RegisterBody(String fbToken){
this.token = fbToken;
}
}
#RequestMapping(value="/register", method = RequestMethod.POST)
public String method0(#RequestBody RegisterBody body){
return body.getToken();
}
}

Your RegisterBody class is private. So nothing outside of the code in your class can create new instances of it. Try:
public static class RegisterBody {
Or, better still, move RegisterBody into its own java file.

The error itself tells you about origin of the problem: no suitable constructor found. So you need to add default constructor into RegisterBody class. Also I'm not sure that making this class private good idea.

It was a combination of both answers #Andremoniy and #Mr Spoon
Worked after I made the class
public static class RegisterBody {
and removed the constructor and made it to an default constructor.
public static class RegisterBody{
private String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
Works now thanks

When Spring tries to resolve Body it creates the object with a default constructor ClassName() and then tries to fill the fields with setters. So if you want to resolve RequestBody into RegisterBody it should look like below:
private class RegisterBody{
private String token;
public RegisterBody() {};
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public RegisterBody(String fbToken){
this.token = fbToken;
}
}
Apart from that remember, that Spring implements MVC for some reason. Put the classes to Utils or Misc package if you can't classify it better but DON'T declare classes in Controllers!

Try to add an empty constructor for the class.

If you're using Lombok you can use the following annotations:
#Getter
#Setter
#AllArgsConstructor
// Must include this annotation since Spring instantiates the #ResponseBody type with no args.
#NoArgsConstructor
public class CustomRequestBody {
private String message;
}
#Slf4j
#RestController
public class ReactiveRestController {
#PostMapping("/api/test")
public Mono<CustomResponse> post(#RequestBody CustomRequestBody requestBody) {
log.info(requestBody.getMessage());
return Mono.just(new CustomResponse(200, "success"));
}
}

Related

Post request with springboot json format

I have a product table, I have a second option table. I cannot manage to create my options for the product at the same time as I create the product. I tried to create the options individually by creating an option table and a category join table. When I send the options in json format it doesn't work. I get the bad request error and in the console:
JSON parse error: Cannot construct instance of
com.pastrycertified.cda.dto.OptionsDto (although at least one
Creator exists): no String-argument constructor/factory method to
deserialize from String value('pie'); nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot
construct instance of com.pastrycertified.cda.dto.OptionsDto
(although at least one Creator exists): no String-argument
constructor/factory method to deserialize from String value
('pie') at [Source:
(org.springframework.util.StreamUtils$NonClosingInputStream); line: 2,
column: 19] (through reference chain:
java.util.LinkedHashMap["typeOption"])]
Thank you for help
data
{
"typeOption": "product",
"ingredients": {
"option1": "test",
"option2":"test1"
}
}
controller option
#RestController
#RequestMapping("/options")
#RequiredArgsConstructor
public class OptionsController {
private final OptionsService optionsService;
#PostMapping("/")
public void save(
#RequestBody Map<String, OptionsDto > options
) {
return ResponseEntity.ok(optionsService.save(options));
}
}
optionService
public interface OptionsService {
Options save(OptionsDto options);
}
optionServiceImpl
#Service
#RequiredArgsConstructor
public class OptionsServiceImpl implements OptionsService {
#Override
public Options save(OptionsDto options) {
Options option = OptionsDto.toEntity(options);
option.setTypeOption(option.getTypeOption());
option.setIngredients(option.getIngredients());
return option;
}
}
optionDto
#Getter
#Setter
#AllArgsConstructor
#Builder
public class OptionsDto {
private Integer id;
private String typeOption;
private String ingredients;
private String nameCategory;
private CategoryDto category;
public static OptionsDto fromEntity(Options options) {
return OptionsDto.builder()
.id(options.getId())
.typeOption(options.getTypeOption())
.ingredients(options.getIngredients())
.nameCategory(options.getCategory().getName())
.build();
}
public static Options toEntity(OptionsDto options) {
return Options.builder()
.id(options.getId())
.typeOption(options.getTypeOption())
.ingredients(options.getIngredients())
.build();
}
}
As Jens mentioned, you need a default constructor in the OptionDto class. Also, you must decide whether ingredients is a String or a Map.
In the controller, you are asking for a Map<> but what you pass in the JSON is not a map. Your controller must be asking for an OptionsDto and not a Map.

Why did not JSON change after I added new fields to POJO

I have added new fields to POJO expecting to find them in response in my Spring Boot application. It is a simple POJO with Lombok annotations:
#Getter
#Setter
#NoArgsConstructor
public class Responce implements AsyncResponse, Serializable {
private String resultCode;
private String errorCode; // added field
public Responce(String resultCode) {
this.resultCode = resultCode;
}
}
In my service method I have created object and then used a setter for added extra field errorCode:
Responce response = new Responce("0");
responce.setErrorCode("777");
But I still receive a JSON with one field:
{
resultCode: "0"
}
How I can force to include new field?
UPD:
AsyncResponse looks like this
public interface AsyncResponse {
String getResultCode();
void setResultCode(String resultCode);
String getErrorCode(); // added getter
void setErrorCode(String errorCode); // added setter
}
There was a filter set up in application.yml in gateway module which hadn't mentioned fields in fieldsToRetain parameter.

Unable to use custom HttpMessageNotReadableException error message in Spring Boot

I'm currently trying to provide custom messages for exceptions, but ran into an issue with HttpMessageNotReadableException.
I have an ErrorDetails class:
public class ErrorDetails {
private Date timestamp;
private String message;
private String details;
public ErrorDetails(Date timestamp, String message, String details) {
super();
this.timestamp = timestamp;
this.message = message;
this.details = details;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
I also have a custom exception handler:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
#RestController
public class CustomizedExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(HttpMessageNotReadableException.class)
#Override
public final ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
ErrorDetails errorDetails = new ErrorDetails(new Date(), "hello",request.getDescription(true));
errorDetails.setMessage("Testing message");
return new ResponseEntity<>(errorDetails,HttpStatus.NOT_ACCEPTABLE);
}
}
But when i try to post a bad request, for example, with a field that should have a integer value I pass a string in the JSON it still returns the default error message of:
{
"timestamp": "2019-03-12T00:15:14.210+0000",
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Cannot deserialize value of type `int` from String \"lala\": not a valid Integer value; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `int` from String \"lala\": not a valid Integer value\n at [Source: (PushbackInputStream); line: 5, column: 17] (through reference chain: com.tdl.model.ToDoNote[\"priority\"])",
"path": "/todos"
}
The JSON request:
{
"name": "An workout",
"dateToComplete": "Today",
"description": "Sleep Day",
"priority": "lala",
"completed": false
}
The desired effect would just be the test message appearing instead of the long description.
I also get this in my Eclipse console:
WARN 16508 --- [nio-5000-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type int from String "lala": not a valid Integer value; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type int from String "lala": not a valid Integer value
at [Source: (PushbackInputStream); line: 5, column: 17] (through reference chain: com.tdl.model.ToDoNote["priority"])]
I changed the status to NOT_ACCEPTABLE just to see more clearly if my custom error is returned.
Any help would be appreciated. Thank you.
EDIT
Added ExceptionHandler for InvalidFormatException, but nothing changed. I still get the default error(exception) message same as before.
#ExceptionHandler(InvalidFormatException.class)
public final ResponseEntity<Object> handleInvalidFormat(InvalidFormatException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
ErrorDetails errorDetails = new ErrorDetails(new Date(), "hello",request.getDescription(true));
errorDetails.setMessage("Testing message");
return new ResponseEntity<>(errorDetails,HttpStatus.NOT_ACCEPTABLE);
}
I ran into this error HttpMessageNotReadableException and I felt the need of customizing it. After a few trials, I ended up with a better and more readable format.
Step 1: Create a Custom Error Details class with the fields that you would want to expose to the client. Below is what I created.
public class ErrorDetails {
private final Date timestamp;
private final String message;
private final String details;
public ErrorDetails(Date timestamp, String message, String details) {
this.timestamp = timestamp;
this.message = message;
this.details=details;
}
// getters not included for brevity
Step 2: Create a class that will extend the ResponseEntityHandler which has the exceptions that can be overridden. Here, override the handleHttpMessageNotReadbale method, and then in the method have an implementation of your own custom error handler.
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}
Step 3: Run your POST or PUT method with the wrong input fields and check the result. For instance, gender is an enum class with only FEMALE and MALE.
{
"firstName":"Dell",
"lastName":"HP",
"birthYear":"2000-02-12",
"email":"dell#gmail.com",
"gender":"BOY"
}
The response is like below:
{
"timestamp": "2022-06-06T08:08:53.906+00:00",
"message": "JSON parse error: Cannot deserialize value of type com.io.clinic.utils.Gender from String "BOY": not one of the values accepted for Enum class: [FEMALE, MALE]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type com.io.clinic.utils.Gender from String "BOY": not one of the values accepted for Enum class: [FEMALE, MALE]\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 6, column: 14] (through reference chain: com.io.clinic.payloadDTO.PatientDTO["gender"])",
"details": "uri=/api/v1/patients"
}
I was satisfied with having the message in that state for debugging but you can also customize the message response in the overridden method.
The problem is solved. I had my custom exception classes in a badly named package. It was called just exception. While it should have been com.app.exception where the whole project is.

Jackson 2.5 - JsonMappingException: Missing external type id property

I have this class with an External Property "contentType":
public class ContentEvent implements AbstractMessagingEvent{
#Valid
#JsonTypeInfo(include = JsonTypeInfo.As.EXTERNAL_PROPERTY, use = NAME, property = "contentType")
public final ContentValue message;
public ContentEvent(ContentValue message) {
this.message = message;
}
public ContentEvent() {
this(null);
}
public static ContentEvent example () {
return new ContentEvent(HostedFile.example());
}
}
"contentType" can be one of the following:
#JsonSubTypes({
#JsonSubTypes.Type(SecureFormSubmission.class),
#JsonSubTypes.Type(SecureFormInvitation.class),
#JsonSubTypes.Type(TextPlain.class),
#JsonSubTypes.Type(HostedFile.class),
#JsonSubTypes.Type(ExternalFile.class)
})
public interface ContentValue{
}
When I try to deserialize a JSON which is missing the "contentType" field, I get the following error:
com.fasterxml.jackson.databind.JsonMappingException: Missing external type id property 'contentType'
I tried adding a 'defaultImpl=NoClass.class' and also a defaultImpl=MyOwnCustomClass' and it clears the error, but the result is an object without any 'contentType'.
What I want is in case the 'contentType' field is missing, to use a default.
Thanks in advance.
You can annotate the class with #JsonIgnoreProperties(ignoreUnknown=true).

how to convert json in POST to javaBean

here is my provider:
class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"
here is my javaBean
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonRootName;
#JsonRootName("issue")
public class TestBean {
#JsonProperty("project_id")
private Integer projectId;
#JsonProperty("subject")
private String subject;
public Integer getProjectId() {
return projectId;
}
public void setProjectId(Integer projectId) {
this.projectId = projectId;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
here is my service
#WebService
public class IssueRestfulApi {
#POST
#Path("/create")
#Consumes(value = {MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public void createIssue(TestBean bean) {
System.out.println(bean.getSubject());
System.out.println("get create request");
}
}
then i send post request like this :
{"issue": {
"project_id": 1,
"subject": "Example"
}
}
finally I get this exception :
Caused by: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "issue" (Class com.rakuten.tranp.api.bean.TestBean), not marked as ignorable
at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1#783478b0; line: 1, column: 12] (through reference chain: com.rakuten.tranp.api.bean.TestBean["issue"])
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:673)
at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:659)
at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1365)
at org.codehaus.jackson.map.deser.BeanDeserializer._handleUnknown(BeanDeserializer.java:725)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:703)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.map.ObjectMapper._readValue(ObjectMapper.java:2704)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1315)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.readFrom(JacksonJsonProvider.java:419)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1311)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBody(JAXRSUtils.java:1262)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameter(JAXRSUtils.java:801)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.processParameters(JAXRSUtils.java:764)
at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:212)
... 28 more
how to solve this question, please help me,
Thank your every much ,
regurd.
Add this property in your application.property file and then try again:
spring.jackson.deserialization.UNWRAP_ROOT_VALUE=true
The error stack trace is straightforward and fair enough to identify the problem:
Unrecognized field "issue" (Class com.rakuten.tranp.api.bean.TestBean)
It means that you are trying to post a JSON with a property ìssue, this is what we can see in your JSON, which doesn't exist in your Java Bean so it causes this Exception because the property isn't recognized and can't be mapped.
Solution:
So you need to change your JSON to include only the inner properties of your issueobject:
{
"projectId": 1,
"subject": "Example"
}
EDIT:
If you can't change your JSON then you need to have two POJO classes:
First one Issue with the two properties projectId and subject
as you wrote in your TestBean class.
Second is TestBean that should only accept an object of type
Issue.

Categories

Resources