SpringアプリケーションのJSONAPI
1. 概要
この記事では、 JSON-API仕様と、それをSpringベースのRESTAPIに統合する方法について説明します。
JavaでJSON-APIのKatharsis実装を使用し、Katharsisを利用したSpringアプリケーションをセットアップするので、必要なのはSpringアプリケーションだけです。
2. Maven
まず、Mavenの構成を見てみましょう。pom.xmlに次の依存関係を追加する必要があります。
<dependency>
<groupId>io.katharsis</groupId>
<artifactId>katharsis-spring</artifactId>
<version>3.0.2</version>
</dependency>
3. ユーザーリソース
次に、ユーザーリソースを見てみましょう。
@JsonApiResource(type = "users")
public class User {
@JsonApiId
private Long id;
private String name;
private String email;
}
ご了承ください:
- @JsonApiResource アノテーションは、リソースUserを定義するために使用されます
- @JsonApiId アノテーションは、リソース識別子を定義するために使用されます
そして、ごく簡単に言えば、この例の永続性は、ここでSpringDataリポジトリになります。
public interface UserRepository extends JpaRepository<User, Long> {}
4. リソースリポジトリ
次に、リソースリポジトリについて説明します。各リソースには、使用可能なAPI操作を公開するためのResourceRepositoryV2が必要です。
@Component
public class UserResourceRepository implements ResourceRepositoryV2<User, Long> {
@Autowired
private UserRepository userRepository;
@Override
public User findOne(Long id, QuerySpec querySpec) {
Optional<User> user = userRepository.findById(id);
return user.isPresent()? user.get() : null;
}
@Override
public ResourceList<User> findAll(QuerySpec querySpec) {
return querySpec.apply(userRepository.findAll());
}
@Override
public ResourceList<User> findAll(Iterable<Long> ids, QuerySpec querySpec) {
return querySpec.apply(userRepository.findAllById(ids));
}
@Override
public <S extends User> S save(S entity) {
return userRepository.save(entity);
}
@Override
public void delete(Long id) {
userRepository.deleteById(id);
}
@Override
public Class<User> getResourceClass() {
return User.class;
}
@Override
public <S extends User> S create(S entity) {
return save(entity);
}
}
ここで簡単に説明します。これはもちろんSpringコントローラーと非常によく似ています。
5. Katharsis構成
katharsis-spring を使用しているため、SpringBootアプリケーションにKatharsisConfigV3をインポートするだけです。
@Import(KatharsisConfigV3.class)
そして、application.propertiesでKatharsisパラメーターを構成します。
katharsis.domainName=http://localhost:8080
katharsis.pathPrefix=/
これで、APIの使用を開始できます。 例えば:
- GET“ http:// localhost:8080 / users “:すべてのユーザーを取得します。
- POST“ http:// localhost:8080 / users “:新しいユーザーなどを追加します。
6. 関係
次に、JSONAPIでエンティティの関係を処理する方法について説明します。
6.1. 役割リソース
まず、新しいリソース– Role を紹介しましょう:
@JsonApiResource(type = "roles")
public class Role {
@JsonApiId
private Long id;
private String name;
@JsonApiRelation
private Set<User> users;
}
次に、UserとRoleの間に多対多の関係を設定します。
@JsonApiRelation(serialize=SerializeType.EAGER)
private Set<Role> roles;
6.2. 役割リソースリポジトリ
非常に迅速に–これがRoleリソースリポジトリです。
@Component
public class RoleResourceRepository implements ResourceRepositoryV2<Role, Long> {
@Autowired
private RoleRepository roleRepository;
@Override
public Role findOne(Long id, QuerySpec querySpec) {
Optional<Role> role = roleRepository.findById(id);
return role.isPresent()? role.get() : null;
}
@Override
public ResourceList<Role> findAll(QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAll());
}
@Override
public ResourceList<Role> findAll(Iterable<Long> ids, QuerySpec querySpec) {
return querySpec.apply(roleRepository.findAllById(ids));
}
@Override
public <S extends Role> S save(S entity) {
return roleRepository.save(entity);
}
@Override
public void delete(Long id) {
roleRepository.deleteById(id);
}
@Override
public Class<Role> getResourceClass() {
return Role.class;
}
@Override
public <S extends Role> S create(S entity) {
return save(entity);
}
}
ここで理解することが重要なのは、この単一のリソースリポジトリはリレーションシップの側面を処理しないということです。つまり、別のリポジトリを使用します。
6.3. リレーションシップリポジトリ
User – Role 間の多対多の関係を処理するには、新しいスタイルのリポジトリを作成する必要があります。
@Component
public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2<User, Long, Role, Long> {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Override
public void setRelation(User User, Long roleId, String fieldName) {}
@Override
public void setRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = new HashSet<Role>();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void addRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = user.getRoles();
roles.addAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public void removeRelations(User user, Iterable<Long> roleIds, String fieldName) {
Set<Role> roles = user.getRoles();
roles.removeAll(roleRepository.findAllById(roleIds));
user.setRoles(roles);
userRepository.save(user);
}
@Override
public Role findOneTarget(Long sourceId, String fieldName, QuerySpec querySpec) {
return null;
}
@Override
public ResourceList<Role> findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) {
final Optional<User> userOptional = userRepository.findById(sourceId);
User user = userOptional.isPresent() ? userOptional.get() : new User();
return querySpec.apply(user.getRoles());
}
@Override
public Class<User> getSourceResourceClass() {
return User.class;
}
@Override
public Class<Role> getTargetResourceClass() {
return Role.class;
}
}
ここでは、リレーションシップリポジトリ内の特異なメソッドを無視しています。
7. テスト
最後に、いくつかのリクエストを分析して、JSON-API出力がどのように見えるかを実際に理解しましょう。
単一のユーザーリソース(id = 2)の取得を開始します。
GET http:// localhost:8080 / users / 2
{
"data":{
"type":"users",
"id":"2",
"attributes":{
"email":"[email protected]",
"username":"tom"
},
"relationships":{
"roles":{
"links":{
"self":"http://localhost:8080/users/2/relationships/roles",
"related":"http://localhost:8080/users/2/roles"
}
}
},
"links":{
"self":"http://localhost:8080/users/2"
}
},
"included":[
{
"type":"roles",
"id":"1",
"attributes":{
"name":"ROLE_USER"
},
"relationships":{
"users":{
"links":{
"self":"http://localhost:8080/roles/1/relationships/users",
"related":"http://localhost:8080/roles/1/users"
}
}
},
"links":{
"self":"http://localhost:8080/roles/1"
}
}
]
}
要点:
- リソースの主な属性はdata.attributesにあります
- リソースの主な関係は、data.relationshipsにあります。
- roles関係に@JsonApiRelation(serialize = SerializeType.EAGER)を使用したため、JSONに含まれ、ノードincludeに含まれています。
次へ–ロールを含むコレクションリソースを取得しましょう。
http:// localhost:8080/rolesを取得します
{
"data":[
{
"type":"roles",
"id":"1",
"attributes":{
"name":"ROLE_USER"
},
"relationships":{
"users":{
"links":{
"self":"http://localhost:8080/roles/1/relationships/users",
"related":"http://localhost:8080/roles/1/users"
}
}
},
"links":{
"self":"http://localhost:8080/roles/1"
}
},
{
"type":"roles",
"id":"2",
"attributes":{
"name":"ROLE_ADMIN"
},
"relationships":{
"users":{
"links":{
"self":"http://localhost:8080/roles/2/relationships/users",
"related":"http://localhost:8080/roles/2/users"
}
}
},
"links":{
"self":"http://localhost:8080/roles/2"
}
}
],
"included":[
]
}
ここでの簡単なポイントは、システム内のすべてのロールをdataノードの配列として取得することです。
8. 結論
JSON-APIは素晴らしい仕様です。最終的に、APIでJSONを使用する方法に構造を追加し、真のハイパーメディアAPIを実際に強化します。
この記事では、Springアプリで設定する1つの方法について説明しました。 しかし、その実装に関係なく、仕様自体は、私の見解では、非常に有望な作業です。
この例の完全なソースコードは、GitHubのから入手できます。 これは、そのままインポートして実行できるMavenプロジェクトです。