Json 在@SpringBootTest中,如何让fasterxml对象映射器包含模型中的字段?

Json 在@SpringBootTest中,如何让fasterxml对象映射器包含模型中的字段?,json,spring,spring-boot-test,fasterxml,spring-boot-2,Json,Spring,Spring Boot Test,Fasterxml,Spring Boot 2,我正在使用SpringBoot2.1和Java11。我已经用fasterxml注释了我的用户模型,这样我的密码可以被POST请求接受,但不会被其他REST请求返回 @Data @Entity @Table(name = "Users") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID

我正在使用SpringBoot2.1和Java11。我已经用fasterxml注释了我的用户模型,这样我的密码可以被POST请求接受,但不会被其他REST请求返回

@Data
@Entity
@Table(name = "Users")
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;
    
    
    private String firstName;
    private String lastName;
    
    @NotBlank(message = "Email is mandatory")
    @Column(unique=true)
    private String email;
    
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;
    private boolean enabled;
    private boolean tokenExpired;
 
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable( 
        name = "users_roles", 
        joinColumns = @JoinColumn(
          name = "user_id", referencedColumnName = "id"), 
        inverseJoinColumns = @JoinColumn(
          name = "role_id", referencedColumnName = "id")) 
    private Collection<Role> roles;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public boolean isAccountNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }    

    @PrePersist @PreUpdate 
    private void prepare(){
        this.email = this.email.toLowerCase();
    }
}

当从读取端点请求我的实体时,如何正确地将我的密码作为映射的一部分包括在内,并禁止使用密码?

这是一个常见的误解,甚至有一个错误

“读”和“写”是从Java属性的角度来理解的,即,当序列化对象时,必须读取该属性,当反序列化该属性时,必须写入该属性


在您的情况下,您想要
@JsonProperty(access=JsonProperty.access.READ_ONLY)

这是一个常见的误解,甚至有一个错误

“读”和“写”是从Java属性的角度来理解的,即,当序列化对象时,必须读取该属性,当反序列化该属性时,必须写入该属性

在您的情况下,您需要
@JsonProperty(access=JsonProperty.access.READ_ONLY)
  • 使用
    WRITE\u ONLY
    READ\u ONLY
    在您的情况下不起作用。原因是http上的一个调用需要两者。让我们以
    this.restemplate.postForEntity
    为例。在发送端,您的
    用户
    java对象需要序列化为json,因此它需要
    读取
    ,当rest端点接收到json时,它需要将json反序列化为
    用户
    java对象,因此需要
    写入
    this.restTemplate.getForEntity的情况也一样

  • 一种解决方案是在返回之前将
    GET
    端点上用户的
    password
    字段设置为null

  • 另一种解决方案是创建一个单独的
    UserDto
    无密码字段,并从
    GET
    端点返回它

  • 另一个解决方案是创建两个
    JsonView
    s,其中一个有密码,另一个没有密码。然后用正确的
    @JsonView

      • 使用
        WRITE\u ONLY
        READ\u ONLY
        在您的情况下不起作用。原因是http上的一个调用需要两者。让我们以
        this.restemplate.postForEntity
        为例。在发送端,您的
        用户
        java对象需要序列化为json,因此它需要
        读取
        ,当rest端点接收到json时,它需要将json反序列化为
        用户
        java对象,因此需要
        写入
        this.restTemplate.getForEntity的情况也一样

      • 一种解决方案是在返回之前将
        GET
        端点上用户的
        password
        字段设置为null

      • 另一种解决方案是创建一个单独的
        UserDto
        无密码字段,并从
        GET
        端点返回它

      • 另一个解决方案是创建两个
        JsonView
        s,其中一个有密码,另一个没有密码。然后用正确的
        @JsonView


      我从“只写”改为“只读”,然后又改回来,但在这两种情况下,我在运行集成测试时都会遇到相同的错误。您是否验证了“密码”是否是生成的JSON的一部分,以及您得到的错误是什么?您的问题只是关于在序列化中包含该数据,这应该可以工作。对于“只读”,通过POST端点保存时的错误是“java.lang.IllegalArgumentException:rawPassword不能为null”(“RegistrationWorksthroughAllayers”测试)。但是GET端点(“GetDetailsAboutIlf”测试)也会失败,因为在JSON中包含了密码,但密码不应该包含在JSON中。我从“只写”更改为“只读”,然后再次更改,但在这两种情况下,我在运行集成测试时都会收到相同的错误。您是否验证了“密码”是否是生成的JSON的一部分,以及您收到的错误是什么?您的问题只是关于在序列化中包含该数据,这应该可以工作。对于“只读”,通过POST端点保存时的错误是“java.lang.IllegalArgumentException:rawPassword不能为null”(“RegistrationWorksthroughAllayers”测试)。但是,GET端点(“GetDetailsAboutIlf”测试)也失败,因为密码在JSON中不应该包含的情况下被包含在JSON中。您可能有一个
      @SpringBootTest
      或一个
      @DataJpaTest
      将两者结合在一起将不起作用(第一个是完全集成测试,另一个只是基于JPA的部分,那么是哪一个呢?)。JSON又有什么问题?您指定在生成JSON时,
      密码
      字段不应该是JSON的一部分,只有在接收到JSON时才应该是。它基本上完全按照您的要求执行。您可以使用
      @springbootest
      @DataJpaTest
      组合这两个字段都不起作用(第一个是完全集成测试,另一个只是一个基于JPA的部分,那么这是哪一个?)。JSON又有什么问题?您指定在生成JSON时,
      password
      字段不应该是JSON的一部分,只有在接收到JSON时才是。它基本上完全按照您的要求执行。
      @SpringBootTest(classes = CardmaniaApplication.class, 
      webEnvironment = WebEnvironment.RANDOM_PORT)
      public class UserControllerIntegrationTest {
          
          @Autowired
      private TestRestTemplate restTemplate;
      
          @Autowired
          private ObjectMapper objectMapper;
      
          @Autowired
          private IUserRepository userRepository;
      
          @Test
          @WithUserDetails("me@example.com")
          void registrationWorksThroughAllLayers() throws Exception {
              final String email = "newuser@test.com";
              final String firstName = "first";
              final String lastName = "last";
              final String password = "password";
              User user = getTestUser(email, password, firstName, lastName, Name.USER);
          
              ResponseEntity<String> responseEntity = this.restTemplate
                  .postForEntity("http://localhost:" + port + "/api/users", user, String.class);
          assertEquals(201, responseEntity.getStatusCodeValue());
      
              final User createdUser = userRepository.findByEmail(email);
              assertNotNull(createdUser);
              assertNotNull(createdUser.getPassword());
          }
      
          @Test
          @WithUserDetails("me@example.com")
          void getDetailsAboutMyself() throws JsonProcessingException, JSONException {
              Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
              UserDetails user = (UserDetails) authentication.getPrincipal();
              final User foundUser = userRepository.findByEmail(user.getUsername());
              ResponseEntity<String> responseEntity = this.restTemplate
                  .getForEntity("http://localhost:" + port + "/api/users/" + foundUser.getId(), String.class);
              assertEquals(200, responseEntity.getStatusCodeValue());
              // assert proper response
              final String userAsJson = objectMapper.writeValueAsString(user);
              assertEquals(userAsJson, responseEntity.getBody());
              JSONObject object = (JSONObject) new JSONTokener(userAsJson).nextValue();
              // Verify no password is returned.
              assertNull(object.getString("password"));
          }
          ...
      }
      
      {"id":null,"firstName":"first","lastName":"last","email":"newuser@test.com","enabled":true,"tokenExpired":false,"roles":null,"username":"newuser@test.com","authorities":null,"accountNonExpired":false,"accountNonLocked":false,"credentialsNonExpired":false}