Here is what i have in my controller.
#RequestMapping(value = "/accountholders/{cardHolderId}/cards/{cardId}", produces = "application/json; charset=utf-8", consumes = "application/json", method = RequestMethod.PUT)
#ResponseBody
public CardVO putCard(#PathVariable("cardHolderId") final String cardHolderId,
#PathVariable("cardId") final String cardId, #RequestBody final RequestVO requestVO) {
if (!Pattern.matches("\\d+", cardHolderId) || !Pattern.matches("\\d+", cardId)) {
throw new InvalidDataFormatException();
}
final String requestTimeStamp = DateUtil.getUTCDate();
iCardService.updateCardInfo(cardId, requestVO.isActive());
final CardVO jsonObj = iCardService.getCardHolderCardInfo(cardHolderId, cardId, requestTimeStamp);
return jsonObj;
}
This is the request body bean:-
public class RequestVO {
private boolean active;
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
The issue that I am having is when i sent the request body as
{"acttttt":true} the active is set to false it updates the cardinfo with false. Whatever wrong key value i sent the active is considered as false. How would I handle this is their a way. Every other scenario is handled by spring with a 404.
Any help or suggestion is appreciated.
Because the default value for primitive boolean is false. Use its corresponding Wrapper, Boolean, instead:
public class RequestVO {
private Boolean active;
// getters and setters
}
If the active value is required, you can also add validation annotations like NotNull:
public class RequestVO {
#NotNull
private Boolean active;
// getters and setters
}
Then use Valid annotation paired with RequestBody annotation to trigger automatic validation process.
Related
Here I have a Rest Controller
#RequestMapping(value = "/mobileNumber", method = RequestMethod.POST, produces = {
MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<ResponseBack> sentResponse() {
return new ResponseEntity<ResponseBack>(ResponseBack.LOGIN_SUCCESS, HttpStatus.ACCEPTED);
}
My Enum Class
public enum ResponseBack {
LOGIN_SUCCESS(0, " success"), LOGIN_FAILURE(1, " failure");
private long id;
private final String message;
// Enum constructor
ResponseBack(long id, String message) {
this.id = id;
this.message = message;
}
public long getId() {
return id;
}
public String getMessage() {
return message;
}
}
When I get the response back from the controller I am getting it as
"LOGIN_SUCCESS"
What I require is
{
"id": "0",
"message": "success"
}
How can I deserialize it to Json and send response, is there any annotation for it.
Please help, thanks.
You must use JsonFormat annotation
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ResponseBack {
...
So you tell that the Json representation of this enum will be the whole object. If you want a specific field to be returned (for example message field) you can annotate the method with JsonValue annotation
#JsonValue
public String getMessage() {
return message;
}
I'm trying to set a defaultValue for a boolean field using MapStruct, but the generated code simply ignores it.
My code:
public class CreateEventRequest {
#NotNull
#JsonProperty
private Boolean isPrivate;
#JsonProperty
private Boolean friendCanInviteFriends;
#JsonProperty
private boolean showGuestList;
public boolean isPrivate() {
return isPrivate;
}
public String getDescription() {
return description;
}
public boolean isFriendCanInviteFriends() {
return friendCanInviteFriends;
}
public boolean isShowGuestList() {
return showGuestList;
}
}
My mapper:
#Mapper(componentModel = "spring")
public interface CreateEventRequestMapper {
#Mapping(target = "showGuestList", source = "showGuestList", defaultExpression = "java(true)")
#Mapping(target = "friendCanInviteFriends", source = "friendCanInviteFriends", defaultValue = "true")
Event map(CreateEventRequest request);
}
The generated code:
public class CreateEventRequestMapperImpl implements CreateEventRequestMapper {
#Override
public Event map(CreateEventRequest request) {
if ( request == null ) {
return null;
}
Event event = new Event();
event.setShowGuestList( request.isShowGuestList() );
event.setFriendCanInviteFriends( request.isFriendCanInviteFriends() );
event.setPrivate( request.isPrivate() );
return event;
}
}
As you can see, I've tried using primitive/non-primitive type but it simply ignores the defaultValue.
Am I missing something here?
Thanks!
The problem is that the return type of your getter methods in the source object is always primitive which can't be null, you need to return Boolean.
MapStruct doesn't support direct private field access which requires reflection.
Hello I want to make an optional parameter on my controller, id that so far looks like this
#ApiOperation(value = "get runs by date in order to paginate")
#ApiResponses({ #ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 500, message = "Unexpected failure") })
#RequestMapping(value = "/time/{date}/to/{toPage}/from/{fromPage}",
params = {"pageSize", "id"},
method = RequestMethod.GET)
public RunCollection getPolicyByDate(GetPolicyByDateRequest request) {
return serviceImpl.getListOfRunsByDate(request);
}
But this means the parameter is required. I want it to be optional. I saw this answer where they use #RequestParam Spring #RequestMapping with optional parameters but I want to include it in my GetPolicyByDateRequest object. Is this possible? The spring docs don't allude to turning off the required attribute. Could I possibly use Optional
2 options :
If your GetPolicyByDateRequest has not primitive attribute, they should be inserted by Spring in your object. If your parameter is not set (i.e there is no pageSize in your url) then Spring injects NULL in your attribute.
public class GetPolicyByDateRequest {
private String date;
private int toPage;
private int fromPage;
private Integer pageSize;
private Integer id;
public GetPolicyByDateRequest() {
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public int getToPage() {
return toPage;
}
public void setToPage(int toPage) {
this.toPage = toPage;
}
public int getFromPage() {
return fromPage;
}
public void setFromPage(int fromPage) {
this.fromPage = fromPage;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String toString() {
return "MyRequest{" +
"date='" + date + '\'' +
", toPage=" + toPage +
", fromPage=" + fromPage +
", pageSize=" + pageSize +
", id=" + id +
'}';
}
}
Weirdly, I think it is some king of bug in Spring, even when using Optional you get a null object if the parameter in the requestParam is not provided.
If you want to get all parameters, you need to use #PathVariable with #RequestParam in your case. Here is how to get your parameters :
#RequestMapping(value = "/time/{date}/to/{toPage}/from/{fromPage}", method = RequestMethod.GET)
public RunCollection getPolicyByDate(#PathVariable String date, #PathVariable int toPage, #PathVariable int fromPage, #RequestParam(required = false) Optional<Integer> pageSize, #RequestParam(required = false) Optional<Integer> id) {
// then use your parameters the way you to use then.
}
Please note :
#PathVariable retrieves the parameter in the URL, where the value is between {}, like {date} -> #PathVariable String date
#RequestParam retrieves the parameter after the ? in the URL. ?pageSize=10 -> #RequestParam int pageSize.
Both of this annotation takes as parameters, among others, required=true|false. When do mark a parameter as not required, make sure you don't use a primitive type, like int, otherwise, you can set null to this object, and your code will fail at runtime. This is the reason why I have used Optional<Integer>, to allow the type. But Spring is clever, and if it detects the Optional in your parameter, then you are forced to use required=false in the #RequestParam.
If you wanted to populate your GetPolicyByDateRequest, then you should have done a POST mapping, and use the annotation #RequestBody GetPolicyByDateRequest request, and send a JSON body in your request to make Spring (actually Jackson) translate your JSON into a GetPolicyByDateRequest object.
Hope it helps
I've stumbled upon interesting case and I'm not sure how to resolve it. It's probably related to JSON Post request for boolean field sends false by default but advices from that article didn't help.
Let's say I have this class:
public class ReqBody {
#NotNull
#Pattern(regexp = "^[0-9]{10}$")
private String phone;
//other fields
#NotNull
#JsonProperty(value = "create_anonymous_account")
private Boolean createAnonymousAccount = null;
//constructors, getters and setters
public Boolean getCreateAnonymousAccount() {
return createAnonymousAccount;
}
public void setCreateAnonymousAccount(Boolean createAnonymousAccount) {
this.createAnonymousAccount = createAnonymousAccount;
}
}
I also have endpoint:
#PostMapping(value = "/test", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<MyOutput> test(
#ApiParam(value = "information", required = true) #RequestBody ReqBody input
) {
//do something
}
problem is when I send my request body as:
{
"phone": "0000000006",
"create_anonymous_account": null
}
or just like
{
"phone": "0000000006"
}
it sets createAnonymousAccount to false.
I have checked, and it correctly recognises "create_anonymous_account": true
Is there any way to "force" null value in boolean field?
I really need to know if it was sent or no, and not to have default value.
You can use Jackson annotation to ignore the null fields. If the Caller doesn't send createAnonymousAccount then it will be null.
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ReqBody {
#NotNull
#Pattern(regexp = "^[0-9]{10}$")
private String phone;
//other fields
#JsonProperty(value = "create_anonymous_account")
private Boolean createAnonymousAccount ;
}
I have the following example:
This is the request body:
public class UserLoginData
implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
//... getter and setters
}
This is the Controller:
#RequestMapping(value = {"/login"}, method = RequestMethod.POST)
#ResponseBody
public LoginResponse login(#RequestBody(required = true) UserLoginData loginData){
//... some code
}
This is how I invoke the service:
POST /login
{"username":"neuquino"}
I expect that Spring returns a HTTP 400 BAD REQUEST error, because password is missing. But instead of that, it returns a HTTP 500 INTERNAL SERVER error with the following stacktrace:
java.lang.NullPointerException
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:948) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838) ~[spring-webmvc-3.2.2.RELEASE.jar:3.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
...
How can I specify to Spring that username and password are required fields in request body?
#Bart's answer was very useful to find my final solution:
public class UserLoginData
implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotBlank
private String username;
#NotNull
#NotBlank
private String password;
//... getter and setters
}
On my Controller I have:
public LoginResponse login(
#RequestBody(required = true) #Valid UserLoginData loginData){
//... login code
}
Until here is very similar, but it is clearer because the controller's method does not have the error validation. Instead of that, I used another class with the ControllerAdvice annotation
#ControllerAdvice
public class RestErrorHandler {
private MessageSource messageSource;
#Autowired
public RestErrorHandler(#Qualifier("messageSource") MessageSource messageSource) {
this.messageSource = messageSource;
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationError processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return this.processFieldErrors(fieldErrors);
}
private ValidationError processFieldErrors(List<FieldError> fieldErrors) {
ValidationError dto = new ValidationError();
for (FieldError fieldError : fieldErrors) {
String localizedErrorMessage = this.resolveLocalizedErrorMessage(fieldError);
dto.getErrors().put(fieldError.getField(), localizedErrorMessage);
}
return dto;
}
private String resolveLocalizedErrorMessage(FieldError fieldError) {
Locale currentLocale = LocaleContextHolder.getLocale();
String localizedErrorMessage = this.messageSource.getMessage(fieldError, currentLocale);
return localizedErrorMessage;
}
}
Now my service is returning this:
{
"errors":{
"country":"country cannot be null"
}
}
I hope it helps someone else.
To get this solution I also used what is written in this post.
If the password is missing it will not be set when the UserLoginData object is created. It will not check if the value is valid or anything. If you need to validate your login data use proper validation.
You could use the annotations in the hibernate validator package for declarative validation e.g.
public class UserLoginData
implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotBlank
private String username;
#NotNull
#NotBlank
private String password;
//... getter and setters
}
Your method could then be written as (note the #Valid annotation):
public LoginResponse login(
#RequestBody(required = true) #Valid UserLoginData loginData,
BindingResult result,
HttpServletResponse response){
if (result.hasErrors()) {
// Validation problems!
response.sendError(400, "Bad login data");
}
}