背景
こちらの記事の続きです。
Docker+Rails6+apipieを使ってAPI定義をSwagger UIに表示 - Qiita
Dart/Flutterでアプリ開発をするメンバーに上記の定義ファイルを送ったところ、「Dartのクライアントコードを自動生成できない」と言われてしまいました。
今回はDartのクライアントコードを自動生成することをゴールに、apipieの設定を書き換えます。
環境
Ruby: 2.7.1
Rails: 6.0.3
Flutter/Dartとは?
Flutterについてざっくりまとめるとこんな感じでしょうか。
- モバイルアプリフレームワークの1つ
- Googleが開発
- Dartという言語を使う
ReactNativeなどはWeb系経験者がアプリに手を出しやすくするものだが、DartはWeb系の経験がなくとも手を出しやすい、ようです。
また、DartはTypeScriptと似たaltJSです。
基本的にFlutter用の言語で、altJSとして使うんだったらTypeScriptを使うほうが良さげ、なようです。
cf. Flutterとは何か? 使うメリットや特徴を理解する (1/4):CodeZine(コードジン)
apipieの調整
さて実装面。
アプリ担当からは以下のエラーを受け取りました。
E/flutter (17797): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: ApiException 500: Exception during deserialization. (Inner exception: ApiException 500: Could not find a suitable class for deserialization)
E/flutter (17797):
E/flutter (17797): #0 ApiClient._deserialize (package:openapi/api_client.dart:57:5)
E/flutter (17797): #1 ApiClient._deserialize.<anonymous closure> (package:openapi/api_client.dart:50:43)
E/flutter (17797): #2 MappedIterator.moveNext (dart:_internal/iterable.dart:392:20)
E/flutter (17797): #3 MapBase._fillMapWithIterables (dart:collection/maps.dart:83:39)
E/flutter (17797): #4 new LinkedHashMap.fromIterables (dart:collection/linked_hash_map.dart:127:13)
E/flutter (17797): #5 ApiClient._deserialize (package:openapi/api_client.dart:49:26)
E/flutter (17797): #6 ApiClient.deserialize (package:openapi/api_client.dart:67:12)
E/flutter (17797): #7 UsersApi.postApiV1UsersToken (package:openapi/api/users_api.dart:77:49)
当方Dartは未経験でして、エラーを見てもなんのこっちゃだったので、アプリ担当に定義ファイルをいじってもらい動くようにしてもらいました。
その結果、以下3つが主な原因とわかりました。
- basePathの指定がない
- "additionalProperties": falseに指定している
- レスポンスにtitleがない
basePathを指定する
api_base_url
が空欄だと警告がでるそうなので追加します。
- config.api_base_url = ""
+ config.api_base_url = "/api"
※ 前回の記事 にて、わざわざapi_base_url
を消していたようです。。
"additionalProperties": falseに指定している
additionalProperties
とは、「propertiesで指定したプロパティ以外も受け付けるかどうか」を指定するオプションのようです。
trueならpropertiesに記載がないプロパティも受け付けるが、falseだとダメなようです。
cf. OpenAPIのadditional_propertyオプションってなに?
クライアントコード生成時にエラーが起きた原因は、これがfalseになっていたから、のようです。
apipieでは初期値がfalseのため、trueに設定しましょう。
+ config.swagger_allow_additional_properties_in_response = true
cf. Swagger-Specific Configuration Parameters
なお、レスポンスする全てのプロパティ(status, message
など意図して返すもの)を含んでいる状態で生成を試みてもエラーは起きるようです。
(ここは未解決...)
レスポンスにtitleがない
API定義(swagger.json)にtitle
がない場合、クラス名がInlineResponse200
となってしまうようです。
"schema": {
"type": "object",
+ "title": "Token",
"properties": {
apipieのリファレンスを見ても該当箇所にtitle
を追加する記述は見つかりませんでした...。
が、 Response Description に似た実装があり、試したところうまくいきました!
+ def_param_group :user_login_token do
+ property :status, Integer, desc: "status code"
+ property :message, String
+ property :detail, String, desc: "user token"
+ end
api :POST, "/v1/users/token", "get access token"
description "ログイン認証をしてトークンを返す"
formats ["json"]
param :email, String, desc: "メールアドレス", required: true
param :password, String, desc: "パスワード", required: true
- returns code: 200, desc: "return user token"
+ returns :user_login_token, desc: "return user token"
error code: 401, desc: "Unauthorized."
以上の修正をすることで、Dartのクライアントコードを生成できるようになりました。
余談:ブラウザからDartのクライアントコードを作る
- Swagger Editor にAPI定義ファイルをペーストする
- ヘッダーの
Generate Client
からdart
を選択する
という方法で、クライアントコードを生成できます。
今回程度の動作確認であれば十分できますね。