#JsonIgnore field is not consuming data from Postman - java

I have used the #JsonIgnore annotation to prevent exposing the password to the user while sending user details to the user:
public class UserDto {
private String username;
#JsonIgnore
private String password;
}
Below is the response of user API:
{
"username": "test12"
}
But while saving the new user when I am hitting the save API and send
below data, my controller method is consuming null password because of
#JsonIgnore and getting null pointer exception;
{
"username": "test1225",
"password": "admin"
}
Controller method:
#RequestMapping(value = "/addAccount", method = RequestMethod.POST)
public ResponseEntity<Void> addAccount(#RequestBody UserDto userDto) {
User user = new User();
user.setUsername(userDto.getUsername());
user.setPassword(userDto.getPassword());
userService.saveUser(user);
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
Is there any way to ignore the Password parameter when returning
response to the user and not to ignore the password field value when
getting password parameter in request body in controller method?

You can use JsonViews to specify which fields should be included during serialization/deserialization. Take a look at this blog post to learn about JsonViews - http://www.baeldung.com/jackson-json-view-annotation.
For your issue, you can create a view called UserResponse.
public class UserResponse {
}
And annotate the fields of UserDto which you want to return with #JsonView(UserResponse.class)
public class UserDto {
#JsonView(UserResponse.class)
private String username;
private String password;
}
And in your controller, add JsonView annotation on the return type.
public #JsonView(UserResponse.class) ResponseEntity<Void> addAccount(#RequestBody UserDto userDto) {

Related

Ignore fields during deserialization in spring?

I want to ignore some fields during deserialization of json data in spring. I cannot use #JsonIgnore as the same model will be used in different methods and different fields need to be ignored.
I have tried to explain the situation with the below example.
class User
{
private String name;
private Integer id;
//getters and setters
}
This is the User class that will be used as model.
#RequestMapping(value = '/path1', method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<CustomResponse> loadUser1(#RequestBody User user){
System.out.println(user.name);
//user.id is not required here
}
This is the first method that will use user.name and ignore user.id.
#RequestMapping(value = '/path2', method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<CustomResponse> loadUser2(#RequestBody User user){
System.out.println(user.id);
//user.name is not required here
}
This is the second method that will use user.id and ignore user.name.
You can use #JsonFilter to achieve dynamic filtering.
First, create a filter using com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter and pass it to a filter provider com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider.
Then, declare this filter on your bean using #JsonFilter.
In your case, this will do it:
#JsonFilter("myFilter")
public class User {
private String name;
private int id;
// Getters and setters
}
This will apply the filter on your POJO:
public MappingJacksonValue getFiltered() {
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("myFilter", SimpleBeanPropertyFilter.filterOutAllExcept("id"));
User user = new User();
user.setId(1);
user.setName("Me");
MappingJacksonValue jacksonValue = new MappingJacksonValue(user);
jacksonValue.setFilters(filterProvider);
return jacksonValue;
}
Edit:
SimpleBeanPropertyFilter has factory methods to tender to almost all practical filtering scenarios. Use them appropriately.

Prevent null check in Mapstruct on update

I'm struggling with form update. Please consider this example:
// Entity with which I need to perform CRUD operations
public class User {
private String name;
private String email;
private String phone;
private String address;
}
I send to UI is UserDTO:
public class UserDTO {
private String name;
private ContactDataDTO contactDataDTO;
}
public class ContactDataDTO {
private String email;
private String phone;
private String address;
}
My mapper:
#Mapper
public interface UserMapper {
#Mappings({
#Mapping(source="email", target="contactDataDTO.email"),
#Mapping(source="phone", target="contactDataDTO.phone"),
#Mapping(source="address", target="contactDataDTO.address")
})
UserDTO userToUserDTO(User user);
#InheritInverseConfiguration
User updateUserFromUserDTO(UserDTO userDTO, #MappingTarget User user);
}
userToUserDTO() works as expected, but generated userDTOToUser() for me seems wierd:
#Override
public User updateUserFromUserDTO(UserDTO userDTO, User user) {
if ( userDTO == null ) {
return null;
}
String address = userDTOContactDataDTOAddress( userDTO );
if ( address != null ) {
user.setAddress( address );
}
String phone = userDTOContactDataDTOPhone( userDTO );
if ( phone != null ) {
user.setPhone( phone );
}
String email = userDTOContactDataDTOEmail( userDTO );
if ( email != null ) {
user.setEmail( email );
}
user.setName( userDTO.getName() );
return user;
}
Problematic use case:
Fill in all fields for User.
Open form again and clear phone field.
That means to backend I will send smth like this:
userDTO: {
name: 'John Doe';
contactDataDTO: {
email: 'johndoe#gmail.com',
phone: null,
address: 'Home'
}
}
So, user.phone won't be updated, as far as I have null check for it in generated code.
I thought NullValueCheckStrategy is what I need, but there no option which fits me.
For now the only option I see - write my own implementation of userDTOToUser() without null checks.
Maybe you can advise better solution, cause for me it looks like a problem that can happen in any mapper for a target update from DTO with non-primitive source.
Runnable demo: https://repl.it/#aksankin/SlateblueUnimportantStack
Thanks a lot.
If you want null to have a certain value for you then you are looking for source presence checking. You can then control in the setPhone of the DTO whether it was set or not and add hasPhone that would use the flag. Then MapStruct will use the presence check method when setting the value.
Probably Optional<> is what you're searching for. In this case you would have null for empty field, Optional if null was send from UI and Optional for actual value. But probably you need to create different DTO for request and response.
try:
#Mapper( )
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper( UserMapper.class );
#Mappings({
#Mapping(source="email", target="contactDataDTO.email"),
#Mapping(source="phone", target="contactDataDTO.phone"),
#Mapping(source="address", target="contactDataDTO.address")
})
UserDTO userToUserDTO(User user);
default void updateUserFromUserDTO(UserDTO userDTO, User user) {
intUpdateUserFromUserDTO( userDTO, userDTO.getContactDataDTO(), user );
}
void intUpdateUserFromUserDTO(UserDTO userDTO, ContactDataDTO contactDataDTO, #MappingTarget User user);
}
(note: I returned void iso a type, which is strictly not needed).

#ModelAttribute for complex objects

I have a user entity that has many attributes (some fields not shown here):
#Entity
public class User {
#OneToOne(cascade = ALL, orphanRemoval = true)
private File avatar; // File is a custom class I have created
#NotEmpty
#NaturalId
private String name;
#Size(min = 6)
private String password;
#Enumerated(EnumType.STRING)
private Role role;
}
In my thymeleaf template I have a form that submits username, password and avatar (MultipartFile). Now in my controller instead of these parameters...
#PostMapping("/register")
public String register(#RequestParam String username,
#RequestParam String password,
#RequestParam MultipartFile avatar) { ...
...I want to use #ModelAttribute #Valid User user. My problem is that:
password first should be encrypted then passed to the user entity,
bytes[] from MultipartFile should be extracted then stored in user entity (as a custom File object),
some other fields such as Role should be set manually in the service class.
How can I take advantage of #ModelAttribute?
Instead of trying to shoehorn everything into your User class, write a UserDto or UserForm which you can convert from/to a User. The UserForm would be specialized for the web and converted to a User later on.
The conversions you are talking about should be done in your controller (as that is ideally only a conversion layer before actually talking to your business services).
public class UserForm {
private MultipartFile avatar;
#NotEmpty
private String username;
#Size(min = 6)
#NotEmpty
private String password;
public UserForm() {}
public UserForm(String username) {
this.username = username;
}
static UserForm of(User user) {
return new UserForm(user.getUsername());
}
// getters/setters omitted for brevity
}
Then in your controller do what you intended to do (something like this):
#PostMapping("/register")
public String register(#ModelAttribute("user") UserForm userForm, BindingResult bindingResult) {
if (!bindingResult.hasErrors()) {
User user = new User();
user.setName(userForm.getUsername());
user.setPassword(encrypt(userForm.getPassword());
user.setAvataor(createFile(userForm.getAvatar());
userService.register(user);
return "success";
} else {
return "register";
}
}
This way you have a specialized object to fix your web based use cases, whilst keeping your actual User object clean.
Maybe you can just use a setter to make all these actions. When Spring is mapping data to fields, and you have setters in the entity, it will use them to pass data. You can preprocess data in this way and set final values to fields.
#PostMapping("/register")
public String register(#ModelAttribute User user, Model model) { // remember if You have another name for parameter and backing bean You should type this one #ModelAttribute(name="userFromTemplate") User user
encryptPassword(user.getPassword); //remember that is sample code, You can do it however You want to
extractBytes(user.getAvatar); //same here
user.setRole(manuallySetRole);
model.addAttribute("user", user);
return "success"; // here u can redirect to ur another html which will contain info about ur user
} else
return "redirect:sorry";
}
encryptPassword(String password) { ... }
same for the rest methods
Here i give You sample code how to use #ModelAttribute in your example. If You have questions feel free to comment.

Passing data from a form to a REST endpoint

I'm kinda new to writing REST endpoints thus this question.
I'm writing a REST endpoint that should support a form submission in iOS that registers an User.
This is my User class.
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long Id;
private String username;
private String email;
private String password;
}
This is the controller user signup signature that I've been asked to work with,
#RestController
public class RegistrationController {
#RequestMapping(value = "/user/signup",
method = RequestMethod.POST,
consumes = {"application/json"})
public ResponseEntity<?> showRegistrationForm(#RequestBody User user) {
try{
//persist the User
return new ResponseEntity(HttpStatus.OK);
}catch(Exception e){
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
}
The user object is provided to me using the following JSON payload,
{
"username": "Something",
"email": "Something",
"password": "password"
}
What I don't understand is how do I grab the fields posted from the form and convert them to the user in a POST request. Wouldn't I need to change the parameters to string, validate them and then compose the User object. Or just what is the right way of doing this.
Any help appreciated.

Error org.springframework.web.HttpMediaTypeNotSupportedException

i have a problem with rest and method post on my controler i have this 2 class the first is user in my class user i have my class with the getters and setter and a default contructor because for the finally I would like use Hibernate .:
#Entity
#Table(name="Utilisateur") // mapping with hibernate (but not using in this situation)
public class User {
#Id
private long id;
#Column(name="nom")
private String nom;
#Column(name="prenom")
private String prenom;
#Column(name="admin")
private boolean admin;
#Column(name="actif")
private boolean actif;
#Column(name="logins")
private String logins;
#Column(name="email")
private String email;
#Column(name="naissance")
private String naissance;
#Column(name="pwd")
private String pwd;
#Column(name="compte")
private String compte;
public User(){
}
/*
with getter and setter.
*/
}
and my class controler (User controller) : is using for make the api principally post api .
#RestController
public class UserController {
#RequestMapping(
value="/api/greetings/post",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces=MediaType.APPLICATION_JSON_VALUE
)
#ResponseBody
public ResponseEntity<User> getByEmail(#RequestBody User user){
if(user==null){
return new ResponseEntity<User>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
and i get this erreur I am using postman for make the query and in parameter of my query I send this Json query :
{"id":"3","nom":"Gille","prenom":"Laurent","admin":"1","actif":"0","logins":"gilaur","email":""toto#hotmail.com,"naissance":"1990/09/09","pwd":"gal","compte":"autre"}
And i get this error :
{"timestamp":1457906727481,"status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'text/plain;charset=UTF-8' not supported","path":"/api/greetings/post/"}
Thank you
you are change headers content-type application/json in Postman because you try set text/plain

Categories