SpringアプリケーションのJSON API

SpringアプリケーションのJSON API

1. 概要

この記事では、the JSON-API specと、それをSpringがサポートするRESTAPIに統合する方法について説明します。

JavaでのJSON-APIのKatharsis実装を使用し、Katharsisを利用したSpringアプリケーションをセットアップするので、必要なのはSpringアプリケーションだけです。

2. メーベン

まず、Mavenの構成を見てみましょう。次の依存関係をpom.xmlに追加する必要があります。


    io.katharsis
    katharsis-spring
    3.0.2

3. ユーザーリソース

次に、ユーザーリソースを見てみましょう。

@JsonApiResource(type = "users")
public class User {

    @JsonApiId
    private Long id;

    private String name;

    private String email;
}

ご了承ください:

  • @JsonApiResourceアノテーションは、リソースUserを定義するために使用されます

  • @JsonApiIdアノテーションは、リソース識別子を定義するために使用されます

そして非常に簡単に–この例の永続性は、ここのSpring Dataリポジトリになります。

public interface UserRepository extends JpaRepository {}

4. リソースリポジトリ

次に、リソースリポジトリについて説明します。各リソースには、使用可能なAPI操作を公開するためのResourceRepositoryV2が必要です。

@Component
public class UserResourceRepository implements ResourceRepositoryV2 {

    @Autowired
    private UserRepository userRepository;

    @Override
    public User findOne(Long id, QuerySpec querySpec) {
        return userRepository.findOne(id);
    }

    @Override
    public ResourceList findAll(QuerySpec querySpec) {
        return querySpec.apply(userRepository.findAll());
    }

    @Override
    public ResourceList findAll(Iterable ids, QuerySpec querySpec) {
        return querySpec.apply(userRepository.findAll(ids));
    }

    @Override
    public  S save(S entity) {
        return userRepository.save(entity);
    }

    @Override
    public void delete(Long id) {
        userRepository.delete(id);
    }

    @Override
    public Class getResourceClass() {
        return User.class;
    }

    @Override
    public  S create(S entity) {
        return save(entity);
    }
}

ここで簡単に説明します–これはもちろんvery similar to a Spring controllerです。

5. カタルシス構成

katharsis-springを使用しているため、必要なのは、Spring BootアプリケーションにKatharsisConfigV3をインポートすることだけです。

@Import(KatharsisConfigV3.class)

そして、application.propertiesでカタルシスパラメータを設定します。

katharsis.domainName=http://localhost:8080
katharsis.pathPrefix=/

これで、APIの使用を開始できます。例えば:

6. 関係

次に、JSONAPIでエンティティの関係を処理する方法について説明します。

6.1. 役割リソース

まず、新しいリソース–Roleを紹介しましょう。

@JsonApiResource(type = "roles")
public class Role {

    @JsonApiId
    private Long id;

    private String name;

    @JsonApiRelation
    private Set users;
}

次に、UserRoleの間に多対多の関係を設定します。

@JsonApiRelation(serialize=SerializeType.EAGER)
private Set roles;

6.2. ロールリソースリポジトリ

非常に迅速–これがRoleリソースリポジトリです:

@Component
public class RoleResourceRepository implements ResourceRepositoryV2 {

    @Autowired
    private RoleRepository roleRepository;

    @Override
    public Role findOne(Long id, QuerySpec querySpec) {
        return roleRepository.findOne(id);
    }

    @Override
    public ResourceList findAll(QuerySpec querySpec) {
        return querySpec.apply(roleRepository.findAll());
    }

    @Override
    public ResourceList findAll(Iterable ids, QuerySpec querySpec) {
        return querySpec.apply(roleRepository.findAll(ids));
    }

    @Override
    public  S save(S entity) {
        return roleRepository.save(entity);
    }

    @Override
    public void delete(Long id) {
        roleRepository.delete(id);
    }

    @Override
    public Class getResourceClass() {
        return Role.class;
    }

    @Override
    public  S create(S entity) {
        return save(entity);
    }
}

ここで理解することが重要なのは、この単一のリソースリポジトリは、別のリポジトリを使用する関係の側面を処理しないということです。

6.3. リレーションシップリポジトリ

UserRole間の多対多の関係を処理するには、新しいスタイルのリポジトリを作成する必要があります。

@Component
public class UserToRoleRelationshipRepository implements RelationshipRepositoryV2 {

    @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 roleIds, String fieldName) {
        Set roles = new HashSet();
        roles.addAll(roleRepository.findAll(roleIds));
        user.setRoles(roles);
        userRepository.save(user);
    }

    @Override
    public void addRelations(User user, Iterable roleIds, String fieldName) {
        Set roles = user.getRoles();
        roles.addAll(roleRepository.findAll(roleIds));
        user.setRoles(roles);
        userRepository.save(user);
    }

    @Override
    public void removeRelations(User user, Iterable roleIds, String fieldName) {
        Set roles = user.getRoles();
        roles.removeAll(roleRepository.findAll(roleIds));
        user.setRoles(roles);
        userRepository.save(user);
    }

    @Override
    public Role findOneTarget(Long sourceId, String fieldName, QuerySpec querySpec) {
        return null;
    }

    @Override
    public ResourceList findManyTargets(Long sourceId, String fieldName, QuerySpec querySpec) {
        User user = userRepository.findOne(sourceId);
        return  querySpec.apply(user.getRoles());
    }

    @Override
    public Class getSourceResourceClass() {
        return User.class;
    }

    @Override
    public Class getTargetResourceClass() {
        return Role.class;
    }
}

ここでは、リレーションシップリポジトリ内の単一のメソッドを無視しています。

7. Test

最後に、いくつかのリクエストを分析して、JSON-API出力がどのように見えるかを実際に理解しましょう。

単一のユーザーリソース(id = 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に含まれ、ノードincludedにあります。

次へ–ロールを含むコレクションリソースを取得しましょう。

{
    "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つの方法を検討しました。 しかし、その実装に関係なく、仕様自体は「私の見解では」非常に非常に有望な作業です。

この例の完全なソースコードは、on GitHubで入手できます。 これは、そのままインポートして実行できるMavenプロジェクトです。