本記事は「Kong Advent Calendar 2024」の10日目のエントリとして、OAS Validation Pluginについて解説する。
APIを開発している時、APIサーバをデプロイしたものの、クライアント側の更新が追いつかず、結果としてサーバ側が期待するリクエストとは異なるリクエストをクライアントが飛ばしてしまうことがある。
これをKongで検知してサーバ側ではなくKongからエラーを返してトラシュを楽にしてくれるのがOAS Validation Pluginである。
どんな感じで使えるのが実際に触って確かめてみる。
検証
ここではチュートリアルを参考にSwagger.ioのSwagger Petstoreを使って検証する。
設定
Petstoreに繋ぐためのKong GatewayのServiceを作成する。
curl -X POST http://localhost:8001/services/ \
--data name='Petstore-Service' \
--data url='https://petstore.swagger.io/v2'
OAS Validation Pluginにはパスのチェックも含まれているため、ServiceとRouteのURLの切り方は少し意識する必要がある。
具体的にはAPIのエンドポイントが
<Serviceのホスト>/<Routeのパス>
となっている時、API Specには<Routeのパス>
だけ含まれている必要がある。
例えば
https://petstore.swagger.io/v2/pet/findByStatus
というアクセス先に対し、API Specで/pet/findByStatus
が定義されている場合、Serviceはhttps://petstore.swagger.io/v2
となる。
次にRouteを作成する。
curl -X POST http://localhost:8001/services/Petstore-Service/routes \
--data name='Petstore-Route' \
--data paths='~/.*' \
--data "strip_path=false"
少し面倒なのが、PluginによるパスチェックはRouteのパスも含まれる。
なので、ここでpaths=~/petstore/.*
などとすると、パスチェックの際に/petstore/
から始まるパスがAPI Specにあるかどうかをチェックしてしまうので、ここでは~/*
としてAPI Specのパスから始まるように調整する。
(多分/petstore/
などの指定もやり方次第で出来ると思うが、今回は確認していない)
また、strip_path
を無効化しないとパスが消えた状態で渡ってしまうのでエンドポイントから404が返ってくる。
単にチェックしたいだけの目的で使うならそれでもいいが、実際にアクセスしたい場合は今回の設定ではfalse
にする必要がある。
次にPluginを設定する。
Pluginでは以下のパラメータを指定する。
-
config.api_spec
:リクエストがAPI Specに沿っているかを確認する際に使うAPI Spec(文字列) -
config.verbose_response
:エラー時にメッセージを詳細化する -
config.api_spec_encoded
: URIエンコードした文字列を渡す場合はtrue
にする
なお、今回はYAMLファイルでAPI Specを渡すが、API SpecをJSONで書いている人もいるかもしれない。
その場合はapi_spec_encoded
は不要になる。
次にValidation対象のAPI Specを用意する。
ここではSwaggerのPetstoreが提供しているAPI Specを利用する。
以下でAPI Specをダウンロードする。
wget https://petstore3.swagger.io/api/v3/openapi.json -O /tmp/swagger_v3.json
今回はYAML形式のSpecを検証するため、JSON形式をYAMLに変換する。
yq -P < /tmp/swagger_v3.json > ./swagger_v3.yaml
PluginをServiceに対して設定する。
curl -X POST http://localhost:8001/services/Petstore-Service/plugins \
--data name='oas-validation' \
--data-urlencode config.api_spec@swagger_v3.yaml \
--data config.verbose_response=true \
--data config.api_spec_encoded=true
--data-urlencode
を使うことでYAMLをURIエンコードした状態で送っている。
動作確認
今回使うPetstoreはペットの情報をAPIで取得できるサンプルのサービスである。
色々なエンドポイントが用意されているが、ここでは/pet/findByStatus
を触ってみる。
このエンドポイントはstatusのクエリが必須となっている。
API Specでは以下のように定義されている。
/pet/findByStatus:
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
produces:
- application/json
- application/xml
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
type: array
ここにクエリなしでリクエストしてみる。
$ curl -X GET "http://localhost:8000/pet/findByStatus" -H "accept: application/json"
すると即座にレスポンスが返ってきた。
{"message":"query 'status' validation failed with error: 'required parameter value not found in request'"}
エラーメッセージには「必須パラメータがリクエストにない」と記載されており、OAS Validation PluginがAPI Specのrequired: true
からパラメータの不足を検知して、エンドポイントに飛ばすことなくエラーを検知してくれた。
なお、エラーコードは400となった。
OAS Validation Pluginがない場合はこんな感じ。
$ curl -X GET "https://petstore.swagger.io/v2/pet/findByStatus" -H "accept: application/xml"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><pets></pets>
エラーコードは200であり、メッセージもエラーではないので原因が特定できない。
このように、OAS Validation Pluginがあればデバッグが少し楽になる。
ちなみにクエリをつけてやると、正常にデータが取得できる。
$ curl -X GET "http://localhost:8000/pet/findByStatus?status=available" -H "accept: application/json" -s | jq . | head -n 5
[
{
"id": 9223372016900015390,
"category": {
"id": 0,
もう少し試してみる。
statusで渡す値がおかしい場合:
$ curl -X GET "http://localhost:8000/pet/findByStatus?status=hoge" -H "accept: application/json"
{"message":"query 'status' validation failed with error: 'failed to validate item 1: matches none of the enum values'"}
パスが違う場合:
$ curl -X GET "http://localhost:8000/findByStatus?status=xxx" -H "accept: application/json"
{"message":"validation failed, path not found in api specification"}
それぞれ、なぜエラーとなったかを知るためのヒントがメッセージとして表示されたので、やはりトラシュはしやすそうだ。
所感
API SpecとあっているかをAPIデプロイ後に簡単に検証できるため、開発時にクライアント側の修正漏れを検知したりするのには便利そう。
一方で、リクエストを止めてAPI Specとリクエストを比較して検証を行うため、性能面に影響がありそうな点は気をつけた方が良さそう。
あくまでも開発時のテスト目的がメインの使用方法と捉え、本番でも使いたい人はしっかり性能試験も行った上で使うのがよいと思われる。