【问题标题】:Using linked Resources in Custom REST Controller with Spring REST JPA使用 Spring REST JPA 在自定义 REST 控制器中使用链接资源
【发布时间】:2018-04-26 10:02:05
【问题描述】:

我正在数据库上开发一组休息资源,并使用 Spring Data Rest 公开核心 CRUD 功能以直接与存储库交互。

在我的简化示例中,我有用户:

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public long id;

    public String name;

    @OneToMany(mappedBy = "user")
    public Collection<Project> projects;
}

和用户自己的项目:

@Entity
public class Project {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public long id;

    public String name;

    public String oneOfManyComplexDerivedProperties;

    @ManyToOne
    public User user;
}

直接与存储库交互很好,因此对于创建用户(其他其他简单实体),问题在于创建项目。项目有大量基于用户表单输入的服务器派生字段,因此我编写了一个自定义控制器来生成它们并保存结果。 为了保持结果,我需要将项目与其拥有的用户相关联。我希望我的客户能够为此使用用户链接,就像通过直接访问存储库创建新实体时一样(直接访问存储库有效):

@RepositoryRestController
public class CustomProjectController {

    @Autowired
    ProjectRepo projectRepo;

    @RequestMapping(value = "/createProject", method = RequestMethod.POST)
    public HttpEntity<Project> createProject(@RequestParam User userResource,
                                         @RequestParam String formField1, // actually an uploaded file that gets processed, but i want simple for example purposes
                                         @RequestParam String formfield2)
{
    Project project = new Project();

    /*
    Actually a large amount of complex business logic to derive properties from users form fields, some of these results are binary.
     */
    String result = "result";
    project.oneOfManyComplexDerivedProperties = result;
    project.user = userResource;
    projectRepo.save(project);

    // aware that this is more complex than I've written.
    return ResponseEntity.ok(project);
}
}

当我打电话时:
@987654321@

我明白了:

{
    "timestamp": 1510588643801,
    "status": 400,
    "error": "Bad Request",
    "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
    "message": "Failed to convert value of type 'java.lang.String' to required type 'com.badger.User'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'http://localhost:9999/api/users/1'; nested exception is java.lang.NumberFormatException: For input string: \"http://localhost:9999/api/users/1\"",
    "path": "/api/createProject"
}

如果我将 userResource 更改为类型 Resource,则会收到不同的错误:
"Failed to convert value of type 'java.lang.String' to required type 'org.springframework.hateoas.Resource'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'org.springframework.hateoas.Resource': no matching editors or conversion strategy found"

我在文档中找不到任何关于在自定义控制器中使用存储库 URI 的参考,我找到的最接近的是 Resolving entity URI in custom controller (Spring HATEOAS),但是自从编写之后 API 已经发生了变化,我无法让它工作。

【问题讨论】:

  • User userResource 需要的是 User 对象,您传入的是字符串 http://localhost:9999/api/users/1

标签: java spring spring-data-rest spring-hateoas


【解决方案1】:

我建议你真正应该做的是:

http://localhost:9999/api/users/1/projects?formField1=data1&formField2=Otherdata

通过启用 Spring Data 的 web 支持,您可以将路径变量自动绑定到实体实例。

@RequestMapping(value = "users/{id}/projects", method = RequestMethod.POST)
        public HttpEntity<Project> createProject(
                            @PathVariable("id") User user,
                            @RequestParam String formField1,
                            @RequestParam String formfield2)
{

}

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web

【讨论】:

  • 这很好用,虽然可惜不适用于以后有 2 个父对象的对象,但我可以找到另一种解决方法。
  • 喜欢绑定到实体的路径变量 - 学到了新东西!
【解决方案2】:

如果您想坚持使用 url 参数,您可以将 @RequestParam User userResource 更改为 @RequestParam String userId

然后在你的 createProject 代码中有类似的东西

User user = userRepo.findOne(userId);
project.user = user;
....
projectRepo.save(project);

如果是我,我会定义一个您传入的 CreateProjectRequest 对象,例如

{
   "userId" : "1",
   "formField1" : "whatever",
   "formField2" : "whatever"
}

并将您的 createProject 更改为

createProject(@RequestBody CreateProjectRequest createProjectRequest)
    ...
    project.setField1(createProjectRequest.getFormField1());
    ...

    User user = userRepo.findOne(createProjectRequest.getUserId());
    project.user = user;
    ....
    projectRepo.save(project);

【讨论】:

  • 如果我不能让 URL 工作,那是我的后备,我一直在诅咒这该死的东西,因为工作的基础设施显然在框架中,他们只是不告诉我我需要提取哪些参数类型/建议/转换器才能使用它。
【解决方案3】:

User userResource 需要的是一个 User 对象,你传入的是一个 String http://localhost:9999/api/users/1

因此,我建议您将请求更改为 POST 请求并将您的用户对象作为该请求的主体传递。

请求的主体看起来像 JSON 格式:

{
    id: 1,
    name: "myName",
    projects: []
}

然后您从您的 URL 中删除 userResource=http://localhost:9999/api/users/1。 最后把@RequestParam User userResource改成@RequestBody User userResource

编辑 由于您的请求中有用户 ID,因此您可以在控制器内拨打电话以通过 ID 查找用户。因此,您可以将用户对象从 @RequestParam User userResource 更改为 @RequestParam long userId,然后在您的方法中调用以查找类似 @​​987654329@ 的用户

所以你的网址看起来像http://localhost:9999/api/createProject?userId=1&amp;formField1=data1&amp;formField2=Otherdata

【讨论】:

  • 我想避免将用户对象嵌入到项目请求中,因为真正的用户对象要复杂得多,而且不止一个。 (直接嵌入,或者嵌入用户 id 然后手动检索是我的后备选项)
  • 我知道它可以使用资源 URL 来完成,因为如果直接通过 rest 直接与存储库交互,它可以无缝工作(我在其他情况下正在这样做,只是这个需要很多处理)
猜你喜欢
  • 1970-01-01
  • 2016-01-22
  • 2018-07-30
  • 1970-01-01
  • 2015-12-07
  • 1970-01-01
  • 2018-06-23
  • 2018-09-04
  • 2017-08-15
相关资源
最近更新 更多