当前位置: 首页 > 知识库问答 >
问题:

混合Spring MVC Spring数据Rest会导致奇怪的MVC响应

姜德泽
2023-03-14

我有两个JPA实体,一个带有SDR导出存储库,另一个带有Spring MVC控制器和非导出存储库。

MVC公开实体具有对SDR管理实体的引用。代码参考见下文。

当从用户控制器中检索用户时,问题就出现了。SDR管理的实体不会序列化,而且Spring可能试图在响应中使用HATEOAS引用。

对于一个完全填充的用户,下面是一个如何获取的:

{
  "username": "foo@gmail.com",
  "enabled": true,
  "roles": [
    {
      "role": "ROLE_USER",
      "content": [],
      "links": [] // why the content and links?
    }
    // no places?
  ]
}

如何清楚地从我的控制器返回带有嵌入式SDR托管实体的User实体?

Spring MVC管理

实体

@Entity
@Table(name = "users")
public class User implements Serializable {

    // UID

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonIgnore
    private Long id;

    @Column(unique = true)
    @NotNull
    private String username;

    @Column(name = "password_hash")
    @JsonIgnore
    @NotNull
    private String passwordHash;

    @NotNull
    private Boolean enabled;

    // No Repository
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
    @NotEmpty
    private Set<UserRole> roles = new HashSet<>();

    // The SDR Managed Entity
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "user_place", 
        joinColumns = { @JoinColumn(name = "users_id") }, 
        inverseJoinColumns = { @JoinColumn(name = "place_id")})
    private Set<Place> places = new HashSet<>();

    // getters and setters
}

回购协议

@RepositoryRestResource(exported = false)
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
    // Query Methods
}

控制器

@RestController
public class UserController {

    // backed by UserRepository
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping(path = "/users/{username}", method = RequestMethod.GET)
    public User getUser(@PathVariable String username) {
        return userService.getByUsername(username);
    }

    @RequestMapping(path = "/users", method = RequestMethod.POST)
    public User createUser(@Valid @RequestBody UserCreateView user) {
        return userService.create(user);
    }

    // Other MVC Methods
}

SDR管理

实体

@Entity
public class Place implements Serializable {

    // UID

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @NotBlank
    private String name;

    @Column(unique = true)
    private String handle;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "address_id")
    private Address address;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "contact_info_id")
    private ContactInfo contactInfo;

    // getters and setters
}

回购协议

public interface PlaceRepository extends PagingAndSortingRepository<Place, Long> {
    // Query Methods
}

共有2个答案

公西英叡
2023-03-14

您可以在控制器中很好地使用@响应实体,然后在响应实体中设置用户对象。

请看下面的例子:

ResponseEntity<User> respEntity = new ResponseEntity<User>(user, HttpStatus.OK);

然后在客户端,您可以调用restTemplate.getForEntity

请参阅下面的restTemplate文档:

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject-java。lang.String-java。lang.Class-java。lang.Object-

拓拔玺
2023-03-14

简而言之:Spring Data REST和Spring HATEOAS劫持了ObjectMapper,并希望将资源之间的关系表示为链接,而不是嵌入资源。

以与另一个实体具有一对一关系的实体为例:

@Entity
public class Person {
    private String firstName;
    private String lastName;
    @OneToOne private Address address;
}

SDR/HATEOAS将返回地址作为链接:

{
    "firstName": "Joe",
    "lastName": "Smith",
    "_links": {
        "self": { "href": "http://localhost:8080/persons/123123123" },
        "address": { "href": "http://localhost:8080/addresses/9127391273" }
    }
}

默认格式可以根据类路径上的内容进行更改。我相信在我的例子中这就是HAL,当你包含SDR和HATEOAS时,这是默认值。根据所述配置,可能不同但相似。

地址由SDR管理时,Spring将执行此操作。如果它根本不是由SDR管理的,它将在响应中包含整个地址对象。我怀疑这本身就解释了您看到的行为。

角色

您没有包含有关UserRole的信息,但根据您的代码,它似乎不是在User之外管理的,因此没有注册Spring Data存储库。如果是这种情况,这就是它被嵌入的原因——没有其他存储库可以“链接”到。

角色下的内容链接看起来像Spring试图像Page一样序列化它。通常内容将有一个资源数组,链接将有诸如“自己”或指向其他资源的链接。我不确定是什么导致的。

位置

Place有自己的Spring数据存储库,因此它将被视为一个托管实体,并链接到而不是嵌入。我怀疑你要找的是投影。检查有关投影的Spring文档。它看起来像这样:

@Projection(name = "embedPlaces", types = { User.class })
interface EmbedPlaces {
    String getUsername();
    boolean isEnabled();
    Set<Place> getPlaces();
}

这应该序列化用户名、enabled和角色,并省略所有其他内容。我个人还没有使用过投影,所以我不能保证它的效果如何,但这是文档中的解决方案。

编辑:当我们在做时,请注意这也适用于创建或更新资源。Spring希望资源作为URL。因此,以人物/地址为例,如果我要创建一个新的人物,我的身体可能会像:

{
    "firstName": "New",
    "lastName": "Person",
    "address": "http://localhost:8080/addresses/1290312039123"
}

很容易忘记这些事情,因为绝大多数的“REST”API都不是REST,SDR/HATEOAS对REST持固执己见的观点(例如,它应该是REST)。

 类似资料:
  • 我在Chrome Developer Tools中出现这个错误已经有一段时间了,我似乎就是找不到不安全的http://请求。它指向我的域,但没有https://。http://www.example.com/不是一个脚本,所以我不明白它是从哪里来的。 内容混杂:“HTTPS://www.example.com/categoy/a-product.html”页面是通过HTTPS加载的,但请求的脚本“

  • 问题内容: 我想我发现了一个错误。也许不是,但是Super CSV不能很好地处理。 我正在使用MapReader解析具有41列的CSV文件。但是,我得到的是CSV- 而使我获得CSV的Web服务错了一行。“标题”行是制表符分隔的行,具有41个单元格。 而且“错误的行”是一个由制表符分隔的行,其中包含36个单元格,其内容没有任何意义。 这是我正在使用的代码: 我在上面提到的行中执行mapReader

  • 问题内容: 我有这段代码: 如您所见,我仅在columnRendered为true时才渲染该outputPanel。 好吧,在某些情况下(仅用于测试以批准其应做的事情): 为true,因此应在c:if中输入,并将columnRendered切换为false。但是事实并非如此,因此columnRendered永远是正确的… 你知道为什么吗? 问题答案: JSF和JSTL不会像您期望的那样同步运行。J

  • 当我的应用程序中的表单提交时,它将(在客户端)转换为超文本标记语言字符串,如下所示: 作为转换过程的一部分,每个字段值都被清理(通过Angular的服务)以删除任何

  • 我正在Apache Camel中使用新的REST DSL,配置如下: getAllMessages方法返回一个没有任何JAXB注释的简单POJO集合。如果我将bindingMode设置为json,API/message可以正常工作,但API/handshake当然不行。 这样混合数据格式可能吗?我需要更改什么才能使它正常工作?

  • 问题内容: 我在名为bot4CA.py的模块上使用cProfile,因此在控制台中键入: 模块运行并退出后,它将创建一个名为Thing.txt的文件,当我打开它时,那里有一些信息,其余的是一堆字符,而不是我想要的整齐的数据文件。有没有人知道如何使用cProfile并最终得到整齐有序的数据表,就像在命令行中正常使用时一样(除了在文件中)?这是.txt文件中某些数据的示例: 我真正想要的是,当您调用c