I'm trying to exclude the possibility of a json field to be modificated at HTTP.POST operation. This is my class:
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class UserModel {
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long userId;
#NotNull
private String username;
private RoleModel role;
#NotNull
private String email;
#NotNull
private String firstName;
#NotNull
private String secondName;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Date registrationDate;
}
I want for example the property userId to be accessible only for read (http get).
I've tried with #JsonProperty but it doesn't work, instead it works for the password field. (this property is visible only for write/ post).
Can you please tell me where I'm wrong? or if there is a more elegant way to do that?
Many thanks,
You can achieve such thing with #JsonView annotation:
// Declare views as you wish, you can also use inheritance.
// GetView also includes PostView's fields
public class View {
interface PostView {}
interface GetView extends PostView {}
}
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class UserModel {
#JsonView(View.GetView.class)
private Long userId;
#JsonView(View.PostView.class)
#NotNull
private String username;
....
}
#RestController
public class Controller {
#JsonView(View.GetView.class)
#GetMapping("/")
public UserModel get() {
return ... ;
}
#JsonView(View.PostView.class)
#PostMapping("/")
public UserModel post() {
return ... ;
}
...
}
For more information: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
Related
I have mongodb collection for following documents:
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
#Document
public class Account {
#Id
private String id;
#Indexed(unique=true)
private String username;
#Indexed(unique=true)
private String email;
#Indexed(unique=true)
private String contact;
private ConfirmationTokenDetails confirmationTokenDetails;
}
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
#Document
public class ConfirmationTokenDetails {
#Indexed(unique=true)
private String confirmationToken;
private LocalDateTime createdAt;
private LocalDateTime expiredAt;
}
Basically all what I want is just get Account entity by confirmationToken attribut which stores in nested ConfirmationTokenDetails object. I tried do that by using following method
public interface AccountRepository extends MongoRepository<Account, String> {
Optional<Account> findByConfirmationTokenDetails_ConfirmationToken(String token);
}
but it wasn't working for me.
I need to use groups so that I can use the same DTO for creating and patching; however, the requirements for these operations are obviously different. Therefore, I decided to use Spirng's #Validated instead of #Valid along with groups. Here is the simplified version of my code:
class PersonDto{
#NotBlank(groups = CreateConstraint.class)
private String phoneNumber;
#NotNull(groups = CreateConstraint.class)
#Valid
private AddressDto address;
}
and in the AddressDto, I have used the default group:
#Getter
#Setter
public class AddressDto {
#NotBlank
private String line1;
private String line2;
private String city;
#NotNull
private Province province;
#NotBlank
private String postalCode;
}
And the controller:
#PatchMapping("/patch")
public void patchPerson(#RequestBody #Validated PersonDto dto) {
personService.patchPerson(dto);}
In the controller here I am using the default group as well. Now, unless I explicitly add #Valid to the AddressDto declaration in PersonDto, the validation is not done for the AddressDto. why is it that #Validated is not cascaded to the fields inside the PersonDto? Thanks.
Edit:
Just to be clear #Valid does validate recursively. wondering why #Validated not...
You have to add the validation group to the cascaded fields as well.
For example, check the line1 field for the CreateConstraint group.
Also, you have to specify the validation group in the controller.
#Getter
#Setter
public class AddressDto {
#NotBlank(groups = CreateConstraint.class)
private String line1;
private String line2;
private String city;
#NotNull()
private Province province;
#NotBlank()
private String postalCode;
}
#PatchMapping("/patch")
public void patchPerson(#RequestBody #Validated({CreateConstraint.class})PersonDto dto) {
personService.patchPerson(dto);
}
I have class:
#Entity
#Data
#Builder(toBuilder = true)
#AllArgsConstructor
#NoArgsConstructor
public class Users extends Model {
#Id
private String id;
private String fullName;
private String country;
private Role role;
public enum Role {
USER;
#Override
#DbEnumValue
public String toString() {
return name().toLowerCase();
}
}
}
It's work good but if I add Jackson annotation #JsonFormat(shape = OBJECT) for enum I have error
#Entity
#Data
#Builder(toBuilder = true)
#AllArgsConstructor
#NoArgsConstructor
public class Users extends Model {
#Id
private String id;
private String fullName;
private String country;
private Role role;
#JsonFormat(shape = OBJECT)
public enum Role {
USER;
#Override
#DbEnumValue
public String toString() {
return name().toLowerCase();
}
}
}
Error:
Caused by: io.ebean.config.BeanNotEnhancedException: Bean class ...Users is not enhanced? Check packages specified in ebean.mf. If you are running in IDEA or Eclipse check that the enhancement plugin is installed. See https://ebean.io/docs/trouble-shooting#not-enhanced
at io.ebeaninternal.server.deploy.BeanDescriptorManager.setEntityBeanClass(BeanDescriptorManager.java:1569)
at io.ebeaninternal.server.deploy.BeanDescriptorManager.createByteCode(BeanDescriptorManager.java:1434)
at io.ebeaninternal.server.deploy.BeanDescriptorManager.readDeployAssociations(BeanDescriptorManager.java:1343)
at io.ebeaninternal.server.deploy.BeanDescriptorManager.readEntityDeploymentAssociations(BeanDescriptorManager.java:761)
at io.ebeaninternal.server.deploy.BeanDescriptorManager.deploy(BeanDescriptorManager.java:365)
at io.ebeaninternal.server.core.InternalConfiguration.<init>(InternalConfiguration.java:208)
at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:120)
at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:36)
at io.ebean.EbeanServerFactory.createInternal(EbeanServerFactory.java:109)
at io.ebean.EbeanServerFactory.create(EbeanServerFactory.java:70)
Use ebean - 12.1.10, jakson - 2.10.1
Can you help me, how I can resolve this problem
So lets say I have User object like this
#Entity
public class User {
#Id
#GeneratedValue
private long id;
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "address", referencedColumnName = "id")
private Address address;
}
#Entity
public class Address {
#Id
#GeneratedValue
private long id;
private String city;
private String country;
}
Now I don't want to write validation annotations in entities. What I would like to do is validate User in #RestController like this
#RestController
public class InvoiceController {
#RequestMapping(value="/users/add", method = RequestMethod.POST)
public Invoice addInvoice(#Validated #RequestBody ValidUser user) {
... do stuff
}
}
The validation annotations would be in ValidUser being like this.
public class ValidUser extends User {
#NotNull
private String name;
#Valid
private Address address;
}
public class ValidAddress extends Address{
#NotNull
private String city;
#NotNull
private String country;
}
The validation works when I remove the address field from the ValidUser but not when it is there. How can I make address validation also work?
I annotated class User with #JsonView and when it returned I see all fields even than that not contains in view class. Here is my class
#Entity
#Table(name = "users")
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private Long userID;
#JsonView(View.Summary.class)
#Column(name="email")
private String email;
#JsonView(View.Summary.class)
#Column(name="user_name")
private String firstName;
#JsonView(View.Summary.class)
#Column(name="user_last_name")
private String lastName;
#JsonView(View.Summary.class)
#Column(name="phone")
private String phone;
#JsonView(View.Summary.class)
#Column(name="origin")
private String address;
#JsonView(View.Summary.class)
#Column(name="birth_date")
private Long birthDate;
#JsonView(View.Summary.class)
#Column(name="gender")
private Long gender;
#JsonView(View.Summary.class)
#Column(name="about_me")
private String aboutMe;
#JsonView(View.SummaryWithPhoto.class)
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="photo")
private Photo avatar;
#JsonView(View.SummaryWithSession.class)
#Transient
private UserSession session;
//getters and setters
Here is my View class
public class View {
public interface Summary {}
public interface SummaryWithPhoto extends Summary {}
public interface SummaryWithSession extends SummaryWithPhoto {}
}
SO then I request get method with #JsonView(View.SummaryWithPhoto.class) annotation I always get userID field but shouldn't. Here is endpoint code
#JsonView(View.SummaryWithPhoto.class)
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<User> getUser(#RequestHeader(value="Access-key") String accessKey,
#RequestHeader(value="Secret-key") String secretKey)
I've spend a some debugging time with the same issue.
Results are:
all fields are included by default if you do not change this behavior (see BeanSerializerFactory.processViews). To change default do:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
fields, marked by #JsonView omitted in result if controller method annotated with OTHER #JsonView (see FilteredBeanPropertyWriter.serializeAsField)
So for your use-case do not change default settings, annotate Long userID by #JsonView and getUser by any other (not the same) View.
Code com\fasterxml\jackson\core\jackson-databind\2.8.4\jackson-databind-2.8.4-sources.jar!\com\fasterxml\jackson\databind\MapperFeature.java
* Feature is enabled by default.
*/
DEFAULT_VIEW_INCLUSION(true)
is contradicted of blog https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring, so I have to look at code closer.