LoginSignup
82
62

More than 1 year has passed since last update.

CRUDのWebApi開発は不要!JsonでWebApiを作れるシステム「versatileapi」を作った話

Last updated at Posted at 2021-07-17

はじめに

タイトルの通りです
定義を設定するだけで、CRUDのRESTWebapiを作れるシステムです

似たようなもので、FirebaseのRealtimeDatabaseやFireStoreがありますが
色々と制限が多く、バリデーションが難しいようだったので作ってみました

Java(Spring Boot)とMongoDBで作りました

ここで使ったシステムです
エンジニア・プログラマにしか使えないSNSを作ってみた話

2020/07/20 ソースコード公開
https://github.com/HawkClaws/versatileapi

フロー図

image.png

フロー詳細

WebApi定義を登録

管理者用WebApiを使用し、API定義を登録します

登録例
apiUrl:APIのURL
apiSecret:認証に使用するシークレットキー
jsonSchema:バリデーションに使用するJsonSchema
methodSettings:各メソッドの設定

number-battle.json
{
    "apiSecret": "",
    "apiUrl":"number_battle",
    "jsonSchema": {
        "additionalProperties":false,
        "type": "object",
        "properties": {
            "name": {
                "description": "名前",
                "type": "string",
                "maxLength": 30
            },
            "secondNumber": {
                "description": "2番目に大きいと予想した数字",
                "type": "number"
            }
        },
        "required": [
            "name",
            "secondNumber"
        ]
    },
    "methodSettings": [
        {
            "httpMethod": "GET",
            "behavior": "Allow"
        },
        {
            "httpMethod": "POST",
            "behavior": "Allow"
        },
        {
            "httpMethod": "PUT",
            "behavior": "NotImplemented"
        },
        {
            "httpMethod": "DELETE",
            "behavior": "NotImplemented"
        }
    ]
}

WebApiを実行

1.URIからリソース・API定義・リソースIDを特定
2.WebApi定義を取得(もちろんOnMemoryキャッシュする)
3.定義を使用してJsonSchemaバリデーション・認証・許可されているメソッドをチェック
4.各メソッドのAPIを実行

ソースコード

VersatileController.java

    @RequestMapping(value = "/api/**")
    public ResponseEntity<Object> versatileApi(HttpServletRequest request)
            throws IOException, InterruptedException, ExecutionException {

        //URLからリソース(DBテーブル)やリソースのID特定
        RepositoryUrlInfo info = urlConverter.getRepositoryInfo(request.getRequestURI());
        String method = request.getMethod();
        String body = request.getReader().lines().collect(Collectors.joining("\r\n"));

        writeLog(request, body);

        ResponseEntity responseEntity = null;
        try {
            //JsonSchemaバリデーション・認証・許可されているメソッドをチェック
            responseEntity = versatileService.checkUseApi(info.repositoryKey, info.id, method, body,
                    request.getHeader(ConstData.AUTHORIZATION));
        } catch (JsonParsingException e) {
            return new ResponseEntity<>(gson.toJson("JsonParseError:" + e.getMessage()), new HttpHeaders(),
                    HttpStatus.BAD_REQUEST);
        }

        if (responseEntity != null)
            return responseEntity;

        Object response = null;
        //各メソッドの処理を実行
        switch (method) {
        case HttpMethods.GET:
            response = versatileService.get(info.id, info.repositoryKey, request.getQueryString());
            if (response == null || Objects.equal(response, "")) {
                return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.NOT_FOUND);
            } else {
                return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.OK);
            }

        case HttpMethods.POST:
            response = versatileService.post(info.id, info.repositoryKey, request.getQueryString(), body,
                    request.getRemoteAddr());
            return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.CREATED);

        case HttpMethods.PUT:
            response = versatileService.put(info.id, info.repositoryKey, request.getQueryString(), body,
                    request.getRemoteAddr());
            return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.OK);

        case HttpMethods.DELETE:
            response = versatileService.delete(info.id, info.repositoryKey, request.getQueryString());
            return new ResponseEntity<>(response, new HttpHeaders(), HttpStatus.NO_CONTENT);

        }
        return new ResponseEntity<>("", new HttpHeaders(), HttpStatus.NOT_IMPLEMENTED);
    }

以上、このような感じです

おまけ

投稿者の中で2番目に大きい数値を当てるゲーム
一番大きい数値を予想して、それより小さい数字、他の数字より大きい数字を当てる
心理的??ゲームです。いや、運ゲーかも

エンドポイント

https://versatileapi.herokuapp.com/api

数値を当てる
POST /number_battle

結果を見る
GET /number_battle/all?$orderby=secondNumber desc&$skip=1&$limit=1

82
62
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
82
62