Spring MVC、REST 和 HATEOAS

发布于 2024-12-01 13:37:35 字数 1827 浏览 6 评论 0原文

我正在努力寻找使用 HATEOAS 实现 Spring MVC 3.x RESTful 服务的正确方法。考虑以下限制:

  • 我不希望我的域实体被 web/rest 结构污染。
  • 我不希望我的控制器被视图构造污染。
  • 我想支持多种观点。

目前我有一个很好地组合在一起的 MVC 应用程序,没有 HATEOAS。域实体是纯 POJO,没有嵌入任何视图或 Web/REST 概念。例如:

class User {
   public String getName() {...}
   public String setName(String name) {...}
   ...
}

我的控制器也很简单。它们提供路由和状态,并委托给 Spring 的视图解析框架。请注意,我的应用程序支持 JSON、XML 和 HTML,但没有域实体或控制器嵌入视图信息:

@Controller
@RequestMapping("/users")
class UserController {

  @RequestMapping
  public ModelAndView getAllUsers() {
    List<User> users = userRepository.findAll();
    return new ModelAndView("users/index", "users", users);
  }

  @RequestMapping("/{id}")
  public ModelAndView getUser(@PathVariable Long id) {
    User user = userRepository.findById(id);
    return new ModelAndView("users/show", "user", user);
  }
}

所以,现在我的问题是 - 我不确定支持 HATEOAS 的干净方法。这是一个例子。假设当客户端请求 JSON 格式的用户时,结果如下:

{
  firstName: "John",
  lastName: "Smith"
}

假设当我支持 HATEOAS 时,我希望 JSON 包含一个简单的“self”链接,客户端可以使用该链接来刷新对象,删除它,或者其他什么。它还可能有一个“朋友”链接,指示如何获取用户的朋友列表:

{
  firstName: "John",
  lastName: "Smith",
  links: [
    {
      rel: "self",
      ref: "http://myserver/users/1"
    },
    {
      rel: "friends",
      ref: "http://myserver/users/1/friends"
    }
  ]
}

不知何故,我想将链接附加到我的对象。我认为执行此操作的正确位置是在控制器层中,因为控制器都知道正确的 URL。此外,由于我支持多个视图,我觉得正确的做法是以某种方式装饰控制器中的域实体,然后再将它们转换为 JSON/XML/Spring 视图解析框架中的任何内容。实现此目的的一种方法可能是使用包含链接列表的通用 Resource 类来包装有问题的 POJO。需要进行一些视图调整才能将其压缩为我想要的格式,但它是可行的。不幸的是,嵌套资源无法以这种方式包装。想到的其他事情包括添加到 ModelAndView 的链接,然后自定义每个 Spring 的开箱即用视图解析器以将链接填充到生成的 JSON/XML/等中。我不想要的是不断地手工制作 JSON/XML/等等。以适应开发过程中出现和消失的各种链接。

想法?

I'm struggling with the correct way to implement Spring MVC 3.x RESTful services with HATEOAS. Consider the following constraints:

  • I don't want my domain entities polluted with web/rest constructs.
  • I don't want my controllers polluted with view constructs.
  • I want to support multiple views.

Currently I have a nicely put together MVC app without HATEOAS. Domain entities are pure POJOs without any view or web/rest concepts embedded. For example:

class User {
   public String getName() {...}
   public String setName(String name) {...}
   ...
}

My controllers are also simple. They provide routing and status, and delegate to Spring's view resolution framework. Note my application supports JSON, XML, and HTML, yet no domain entities or controllers have embedded view information:

@Controller
@RequestMapping("/users")
class UserController {

  @RequestMapping
  public ModelAndView getAllUsers() {
    List<User> users = userRepository.findAll();
    return new ModelAndView("users/index", "users", users);
  }

  @RequestMapping("/{id}")
  public ModelAndView getUser(@PathVariable Long id) {
    User user = userRepository.findById(id);
    return new ModelAndView("users/show", "user", user);
  }
}

So, now my issue - I'm not sure of a clean way to support HATEOAS. Here's an example. Let's say when the client asks for a User in JSON format, it comes out like this:

{
  firstName: "John",
  lastName: "Smith"
}

Let's also say that when I support HATEOAS, I want the JSON to contain a simple "self" link that the client can then use to refresh the object, delete it, or something else. It might also have a "friends" link indicating how to get the user's list of friends:

{
  firstName: "John",
  lastName: "Smith",
  links: [
    {
      rel: "self",
      ref: "http://myserver/users/1"
    },
    {
      rel: "friends",
      ref: "http://myserver/users/1/friends"
    }
  ]
}

Somehow I want to attach links to my object. I feel the right place to do this is in the controller layer as the controllers all know the correct URLs. Additionally, since I support multiple views, I feel like the right thing to do is somehow decorate my domain entities in the controller before they are converted to JSON/XML/whatever in Spring's view resolution framework. One way to do this might be to wrap the POJO in question with a generic Resource class that contains a list of links. Some view tweaking would be required to crunch it into the format I want, but its doable. Unfortunately nested resources could not be wrapped in this way. Other things that come to mind include adding links to the ModelAndView, and then customizing each of Spring's out-of-the-box view resolvers to stuff links into the generated JSON/XML/etc. What I don't want is to be constantly hand-crafting JSON/XML/etc. to accommodate various links as they come and go during the course of development.

Thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鯉魚旗 2024-12-08 13:37:35

GitHub 上有一个名为 Spring HATEOAS 的有用项目,其描述如下:

“该项目提供了一些 API 来简化创建 REST 表示形式
使用 Spring 时遵循 HATEOAS 原则
特别是 Spring MVC”

如果您返回的资源类扩展了“ResourceSupport”,您可以轻松地向其添加链接,并且可以使用“ControllerLinkBuilder”构建链接,例如添加自链接:

import static org.sfw.hateoas.mvc.ControllerLinkBuilder.*;

Link link = linkTo(YourController.class).slash(resource.getName()).withSelfRel();
resource.add(link);

这是一个相当新的项目,但它是如果需要,可从公共 Maven 存储库获取:

<dependency>
    <groupId>org.springframework.hateoas</groupId>
    <artifactId>spring-hateoas</artifactId>
    <version>0.3.0.RELEASE</version>
</dependency>

如果您使用 Maven 工件:

org.sfw.hateoas.mvc.ControllerLinkBuilder

则变为:

org.springframework.hateoas.mvc.ControllerLinkBuilder

There is a useful project called Spring HATEOAS on GitHub which has the following description:

"This project provides some APIs to ease creating REST representations
that follow the HATEOAS principle when working with Spring and
especially Spring MVC"

If the resource class you are returning extends 'ResourceSupport' you can easily add links to it, and you can build links using 'ControllerLinkBuilder', for example to add a self link:

import static org.sfw.hateoas.mvc.ControllerLinkBuilder.*;

Link link = linkTo(YourController.class).slash(resource.getName()).withSelfRel();
resource.add(link);

It is quite a new project but it is available from the public Maven repo if required:

<dependency>
    <groupId>org.springframework.hateoas</groupId>
    <artifactId>spring-hateoas</artifactId>
    <version>0.3.0.RELEASE</version>
</dependency>

If you use the maven artifact:

org.sfw.hateoas.mvc.ControllerLinkBuilder

becomes:

org.springframework.hateoas.mvc.ControllerLinkBuilder
场罚期间 2024-12-08 13:37:35

我的想法:

  • 使用某种命名约定,例如可以根据对象的类名构造自引用 url。
  • 我不认为添加链接的东西应该由控制器添加(顺便说一句,你自己写的“我不希望我的控制器被视图构造污染”。我会尝试寻找一种方法来扩展 JSON 序列化,以便它自动添加额外的东西。您可能需要向实体添加一些注释,即使这会稍微污染它们。

My Thoughts:

  • using some kind of naming convention so that for example the self reference url can be constructed out of the class name of the object.
  • I do not think that adding the links stuff should be added by the controller (btw you self wrote "I don't want my controllers polluted with view constructs". I would try to search for an way to extend the JSON serialization so that it automatically add the extra stuff. May you need to add some annotations to your entities, even if this would pollute them a bit.
旧时模样 2024-12-08 13:37:35

只是在寻找其他内容时偶然发现了这一点,并认为您应该考虑使用 Link 标头而不是 JSON 正文中的内容,这实际上只会污染资源的表示形式。

查看关于网络链接的 IETF 备忘录以及链接关系的 IANA 注册表

Just stumbled across this while looking for something else and thought you should be considering using the Link header instead of content in the JSON body which is really just polluting the representations of your resources.

Check out the IETFs memo on web linking and also the IANA registry of link relations.

╭ゆ眷念 2024-12-08 13:37:35

要在您的 REST api 中创建链接,您可以使用 Spring 框架的 HAETOAS 项目。

org.springframework.hateoas.mvc.ControllerLinkBuilder 类有一组方法,您可以使用它们来构建链接,例如 -

Link link=linkTo(PersonControllerImpl.class).slash(null).withSelfRel();

另外,如果您有一个具有 @RequestMapping 注释的控制器方法一些 URI 值 -

@RequestMapping(value = "/customer", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<?> getCustomer() {

//...
}

那么您可以使用方法 URI 值创建链接 -

linkTo(methodOn(PersonControllerImpl.class).getCustomer()).toUri().toString()

它将返回 String 值 (http://www.urhost.com/customer),您可以设置在你的实体 对象

TO create the link in your REST api you can use HAETOAS project of Spring framework.

org.springframework.hateoas.mvc.ControllerLinkBuilder class have set of method which you can use to build a link like -

Link link=linkTo(PersonControllerImpl.class).slash(null).withSelfRel();

Also if you have a controller method having @RequestMapping annotation with some URI value -

@RequestMapping(value = "/customer", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<?> getCustomer() {

//...
}

then you can create link using method URI value as -

linkTo(methodOn(PersonControllerImpl.class).getCustomer()).toUri().toString()

it will return String value (http://www.urhost.com/customer) which you can set in your entity Object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文