Web

実践入門「OpenAPI」とは?

投稿日:2023年11月22日 更新日:

OpenAPIは、REST APIのインターフェースの定義の説明を記述するフォーマットを規定する規格です。フォーマットは、YAML形式とJSON形式の2種類あって、APIの仕様を記述します。これを解釈するツールを使えば、APIドキュメントを生成できます。

■ OpenAPIの存在意義

共通のOpenAPIのフォーマットを使ってインタフェース定義を記述することで、APIのドキュメント化、呼び出し側であるクライアントコードの自動生成、テストの自動実行など、様々なREST APIを使用するための共通環境・ツールを整備することができます。

REST APIを開発する側も、これらの共通環境・ツールを使えば、わざわざ自前で準備する必要がないため、APIの開発に注力できます。

■ OpenAPIの定義の記述

例として、以下の記事で紹介したREST APIのサーバ側のサンプルコードを使って、コードからOpenAPIのフォーマットを逆に生成してみます。


ChatGPT4により、以下のプロンプトを入力し、YAML形式とJSON形式でOpenAPIフォーマットを生成します。

プロンプト:「以下のREST APIのOpenAPIの定義をYAMLとJSONで出力すること。」

# -*- coding: utf-8 -*-
from werkzeug.security import generate_password_hash, check_password_hash
from flask_httpauth import HTTPBasicAuth
import json
from datetime import timedelta
from flask_jwt_extended import JWTManager, jwt_required, create_access_token
from flask_restx import Api, Resource
from flask import Flask, make_response, jsonify, request

# BASIC認証インスタンスを生成
auth = HTTPBasicAuth()

# BASIC認証用ユーザー情報
users_new = {
    "user001": generate_password_hash("secret001"),
    "user002": generate_password_hash("secret002")
}

# BASIC認証用パスワードチェック関数
@auth.verify_password
def verify_password(username, password):
    if username in users_new and \
            check_password_hash(users_new.get(username), password):
        return username


# FLASKアプリのインスタンスの生成とJWTトークンの生成のパラメータの設定
def create_app():
    app = Flask(__name__)
    app.config['JWT_SECRET_KEY'] = 'SecretKey'
    # 暗号化署名のアルゴリズム
    app.config['JWT_ALGORITHM'] = 'HS256'
    # 有効期限に対する余裕時間
    app.config['JWT_LEEWAY'] = 0
    app.config['JWT_EXPIRATION_DELTA'] = timedelta(seconds=300)     # トークンの有効期間
    app.config['JWT_NOT_BEFORE_DELTA'] = timedelta(seconds=0)       # トークンの使用を開始する相対時間
    return app


# FLASKアプリのインスタンスの生成
app = create_app()

# FLASK REST APIインスタンスの生成
api = Api(app)

# JWTインスタンスの生成
jwt = JWTManager(app)


#
# トークン取得エンドポイント
#
@app.route('/token', methods=['POST'])
@auth.login_required    # BASIC認証
def index():
    if request.method == 'POST':
        grant_type = request.form['grant_type']
        if grant_type == 'client_credentials':
            access_token = create_access_token(identity=auth.username())
            return make_response(jsonify({'access_token': access_token}), 200)
    
    return make_response(jsonify({'message': 'bad request'}), 500)

#
# REST APIエンドポイント
#
@api.route('/restapi', '/restapi/<string:id>', methods=['POST','GET','PUT','DELETE'])
class TsetSecureController(Resource):   # Resourceクラスを継承し、HTTPメソッド処理を定義
    @jwt_required()                     # トークン(JWT)認証
    def post(self):                     # POSTメソッド
        json_data = request.json
        print(json_data)

        response_json_dic = {
            'id': 'ID_A001',
            'message': 'Create data by endpoint(POST).'
        }
        return make_response(jsonify(response_json_dic), 200)

    def get(self, id=None):             # GETメソッド
        if id is not None:

            response_json_dic = {
                'id': 'ID_A001',
                'message': 'Refer data by endpoint(GET).'
            }
            return make_response(jsonify(response_json_dic), 200)
        else:
            return make_response(jsonify({'message': 'bad id'}), 500)

    def put(self, id=None):             # PUTメソッド
        if id is not None:
            
            json_data = request.json
            print(json_data)

            response_json_dic = {
                'id': 'ID_A001',
                'message': 'Update data by endpoint(PUT).'
            }
            return make_response(jsonify(response_json_dic), 200)
        else:
            return make_response(jsonify({'message': 'bad id'}), 500)

    def delete(self, id=None):          # DELETEメソッド
        if id is not None:
            response_json_dic = {
                'id': 'ID_A001',
                'message': 'Delete data by endpoint(DELETE).'
            }
            return make_response(jsonify(response_json_dic), 200)
        else:
            return make_response(jsonify({'message': 'bad id'}), 500)

・YAML形式

openapi: 3.0.0
info:
  title: Sample Flask REST API
  version: 1.0.0
paths:
  /token:
    post:
      summary: Acquire JWT token
      security:
        - basicAuth: []
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                grant_type:
                  type: string
                  enum: [client_credentials]
      responses:
        '200':
          description: JWT token provided
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
        '500':
          description: Bad request
  /restapi/{id}:
    post:
      summary: Create data
      security:
        - jwtAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Data created
    get:
      summary: Retrieve data
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Data retrieved
        '500':
          description: Bad ID
    put:
      summary: Update data
      security:
        - jwtAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Data updated
        '500':
          description: Bad ID
    delete:
      summary: Delete data
      security:
        - jwtAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Data deleted
        '500':
          description: Bad ID
components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
    jwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

・JSON形式

{
  "openapi": "3.0.0",
  "info": {
    "title": "Sample Flask REST API",
    "version": "1.0.0"
  },
  "paths": {
    "/token": {
      "post": {
        "summary": "Acquire JWT token",
        "security": [
          {
            "basicAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": ["client_credentials"]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JWT token provided",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Bad request"
          }
        }
      }
    },
    "/restapi/{id}": {
      "post": {
        "summary": "Create data",
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Data created"
          }
        }
      },
      "get": {
        "summary": "Retrieve data",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Data retrieved"
          },
          "500": {
            "description": "Bad ID"
          }
        }
      },
      "put": {
        "summary": "Update data",
        "security": [
          {
            "jwtAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],

■ OpenAPIフォーマットのドキュメント表示

上記のYAML形式、JSON形式のOpenAPIフォーマットを解釈し、ドキュメント表示してくれるツールを使います。複数のツールがありますが、その一つにAPIDOCがあります。

APIDOCにGoogleアカウントとサインインします。

個人プロジェクトを選択し、その他の機能からインポートを選びます。

上記で生成したOpenAPIフォーマットのファイル(test_restapi_server.yaml、または、test_restapi_server.json)を選択し、インポートします。

インポートすると、APIのメニューが左側に表示され、右側に選択したAPIの詳細が表示されます。

また、右上にある「コード生成」ボタンをクリックすると、REST APIを呼び出すためのクライアントコードが言語毎に生成されます。

■ まとめ

OpenAPIのフォーマットを使った共通のツールがいろいろあるようです。つまり、OpenAPIに従ってREST APIのインタフェース定義をすることで、多くのメリットを享受できるということです。

OpenAPIに従って定義をわざわざ記載することが、面倒に思われる人もいると思います。
ChatGPTを使えば、サーバのコードからリバースエンジニアリングできますので、このような手間を書けなくても、そのメリットを享受できます。



ソフトウェア開発・システム開発業務/セキュリティ関連業務/ネットワーク関連業務/最新技術に関する業務など、「学習力×発想力×達成力×熱意」で技術開発の実現をサポート。お気軽にお問合せ下さい

-Web

執筆者:


comment

メールアドレスが公開されることはありません。

関連記事

実践入門「gRPC」とは?

HTTPを使ったAPI定義には、REST APIとGraphQLがあります。これらのAPI定義は、外部に公開するための「外向け」のものです。それに対し、「内向け」のプロセス間通信(IPC)のAPI定義 …

リモート会議API「Zoom REST API」の実践入門(Server -To-Server OAuth)

リモート会議を開催するとき、アプリケーションのZoomを使われてる人も多いと思います。Zoomには、Pythonによってプログラミング可能なWeb-APIであるREST APIが用意されています。この …

はじめての「Webシステム」入門

ネットワークを使ったシステムは、クライアント/サーバ型システムが基本です。クライアント/サーバ型システムとは、ユーザが操作するクライアントと、データを管理し、処理を実行するサーバとの間をネットワークで …

実践入門「スクレイピング」とは?(その1)

インターネットにあるWebサイトのHTMLデータの中から、必要なデータを自動的に取得することをスクレイピングといいます。Googleなどの検索エンジンがインデックスを作成するために、自動でWebサイト …

実践入門「GraphQL」とは?

サービスを定義する手段として、REST APIの他、GraphQLがあります。これは、データベースのデータを操作するためのSQLのように、サーバ内にあるデータを直接的にアクセスするようなインタフェース …

Chinese (Simplified)Chinese (Traditional)EnglishFilipinoFrenchGermanHindiJapaneseKoreanMalayThaiVietnamese