문제 설명:
Spring MVC 기반의 RESTful 인터페이스에서 요청 파라미터 Bean 또는 인터페이스에서 반환된 엔티티 클래스의 필드가 @JsonProperty(access = Access.WRITE_ONLY)에 의해 제약을 받는 경우 나머지를 호출하여 해당 필드의 값을 얻을 수 있다. Postman을 통한 인터페이스, 즉 get/set은 문제가 되지 않습니다.
feign 인터페이스를 통해 호출하면 필드가 바이트코드로 직렬화되어 서비스가 있는 머신으로 전송되며, 필드가 역직렬화되면 획득한 필드 값은 null이 됩니다.
이유:
Access.WRITE_ONLY로 제한되는 필드 값은 직렬화되지 않으므로 상대방이 얻은 값은 null입니다. Postman이 서버를 직접 조정할 때 값을 직접 바이트코드로 직렬화하므로 값을 얻을 수 있습니다.
해결책:
제약 조건 필드의 Bean을 버리고 액세스를 위한 새 Bean을 생성하고 feign 인터페이스를 호출합니다.
특정 문제 사례 및 솔루션: (feign 호출은 사용자 역할 역할을 얻을 수 없지만 컨트롤러 자체는 이를 얻을 수 있습니다.)
사용자 모듈의 사용자 엔터티 클래스 및 dto 클래스:
package com.byx.scaffold.user.entity;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import com.byx.scaffold.common.entity.jpa.BaseEntity;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import lombok.Data;
import lombok.ToString;
import lombok.EqualsAndHashCode;
/** 用户 */
@ToString(exclude = "password")
@Data
@EqualsAndHashCode(callSuper=false)
@Entity
@Table(name = "app_user")
public class User extends BaseEntity {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(length = 50, unique = true, nullable = false)
private String username;
@Column(length = 50)
private String name;
/** 头像 **/
private String avatar;
/** 手机号码 */
@Column(unique = true)
private String phone;
/** 邮箱 */
private String email;
/** 用户所属的组织类型(公司、政府部门) */
private String orgType;
/** 组织名称(可能是公司,也可能是政府部门) */
private String orgName;
@JsonProperty(access = Access.WRITE_ONLY )
@Column(nullable = false)
private String password;
/** 账号禁用 */
private boolean disabled;
@JsonProperty(access = Access.WRITE_ONLY )
@ManyToMany
@JoinTable(name = "app_user_role", joinColumns = @JoinColumn(name= "user_id"), inverseJoinColumns = @JoinColumn(name= "role_id"))
private List<Role> roles;
public void replaceRoles(List<Role> roles) {
this.roles.clear();
if (roles != null) {
this.roles.addAll(roles);
}
}
public User() {}
public User(Integer id, String username, String name) {
this.id = id;
this.username = username;
this.name = name;
}
}
package com.byx.scaffold.user.dto;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.byx.scaffold.user.entity.Role;
import com.byx.scaffold.user.entity.User;
import lombok.Data;
import javax.persistence.Column;
@Data
public class UserDTO {
private Integer id;
private String username;
private String name;
private String avatar;
private String phone;
private String email;
private boolean disabled;
private List<Role> roles;
/** 用户所属的组织类型(公司、政府部门) */
private String orgType;
/** 组织名称(可能是公司,也可能是政府部门) */
private String orgName;
public UserDTO(User user) {
this.id = user.getId();
this.username = user.getUsername();
this.name = user.getName();
this.avatar = user.getAvatar();
this.phone = user.getPhone();
this.email = user.getEmail();
this.disabled = user.isDisabled();
this.orgType = user.getOrgType();
this.orgName = user.getOrgName();
Stream<Role> stream = user.getRoles().stream();
this.roles = stream.map(r -> {
Role role = new Role();
role.setId(r.getId());
role.setName(r.getName());
return role;
}).collect(Collectors.toList());
}
public UserDTO(String username, String name) {
this.username = username;
this.name = name;
}
}
controller类
/**
* 根据用户名查询用户详细信息
* @param username
* @return
*/
@GetMapping("/user-by-username")
public UserDTO getUserInfo(String username) {
return userService.selectUserInfo(username);
}
serviceimpl类
@Override
public UserDTO selectUserInfo(String username) {
Optional<User> optional = userRepo.findByUsername(username);
return optional.map(UserDTO::new).orElse(null);
}
공통 공용 클래스 dto 엔터티:
package com.byx.scaffold.common.dto;
import com.byx.scaffold.common.entity.user.Role;
import lombok.Data;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Data
public class UserDTO {
private Integer id;
private String username;
private String name;
private String avatar;
private String phone;
private String email;
private boolean disabled;
private List<Role> roles;
/** 用户所属的组织类型(公司、政府部门) */
private String orgType;
/** 组织名称(可能是公司,也可能是政府部门) */
private String orgName;
}
가짜 인터페이스:
package com.byx.scaffold.context.feign;
import com.byx.scaffold.common.dto.MessageDTO;
import com.byx.scaffold.common.dto.UserDTO;
import com.byx.scaffold.common.response.BaseResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(value = "user")
public interface IUserFeign {
/**
* 根据用户名获取用户详细信息
*
* @return
*/
@GetMapping("user/user/user-by-username")
UserDTO getUserInfo(String username);
}