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を使えば、サーバのコードからリバースエンジニアリングできますので、このような手間を書けなくても、そのメリットを享受できます。
ソフトウェア開発・システム開発業務/セキュリティ関連業務/ネットワーク関連業務/最新技術に関する業務など、「学習力×発想力×達成力×熱意」で技術開発の実現をサポート。お気軽にお問合せ下さい