こんにちは、阪本です。
以前、「Springfox+Swagger+Bootprintによる即席REST API仕様書作成」というエントリーを書きましたが、今回はパラメータの制約をドキュメントに反映する方法について確認してみます。
なお、今回はSpringfoxのバージョンを2.3.1にしています。
@ApiModelPropertyによる制約の指定
まずは、Swagger Annotationを使ってパラメータの制約や説明の追加を行ってみます。
前回使用したEmployeeクラスに、@ApiModelPropertyアノテーションを追加します。
package swagger.entity; import java.util.Date; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.Range; import io.swagger.annotations.ApiModelProperty; public class Employee { private Integer id; private String name; private Date birthday; @ApiModelProperty(value = "Employee ID.", allowableValues = "range[1, 100]") @Range(min = 1, max = 100) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } @ApiModelProperty(value = "Employee's name.", required = true, allowableValues = "range[0, 32]") @NotEmpty @Size(max = 32) public String getName() { return this.name; } public void setName(String name) { this.name = name; } @ApiModelProperty(value = "Employee's birthday with ISO 8601 format.", required = true) @NotNull public Date getBirthday() { return this.birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
value属性にプロパティの説明、requiredに必須かどうか、allowableValuesに値の範囲を指定します。他にも、例を指定したりプロパティ名を変更したりもできますが、ここでは割愛します。
上のようにEmployeeクラスを記述してSpringBootアプリケーションを起動すると、次のようにSwagger UIに反映されます。
http://localhost:8080/swagger-ui.html
画面右側のModel部分に情報が反映されていることがわかります。idプロパティのinteger部分にカーソルを合わせると、allowableValuesに指定した範囲も見えます。
ちなみに、日付フォーマット等の、必須・範囲以外の制約については、value属性に説明として記述する必要があります(専用の属性がありません)。
バリデーション用のアノテーションから制約条件を取得する
上で説明した方法では、JSR-303のアノテーション(@NotNullや@Size等)の情報を取ってきているのではなく、あくまで@ApiModelPropertyの値を取ってきてドキュメントを生成しているに過ぎません。
プログラムで動作するバリデーション用アノテーションとドキュメント用アノテーションを両方記述するのは、手間なのとズレが発生するのとで、避けたいところです。
ということで、バリデーション用のアノテーション(JSR-303等)から制約条件を取得できるように修正を加えます。
残念ながら、現時点(2016年1月)の最新バージョンであるSpringfox 2.3やSwagger 1.5では実現できないため、自前でコードを書く必要があります。
具体的には、ModelPropertyBuilderPluginインタフェースを実装したクラスを作成します。@Componentを付けて、SpringBootのComponentScan対象にしておきます。
package swagger; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.Range; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.google.common.base.Optional; import springfox.documentation.builders.ModelPropertyBuilder; import springfox.documentation.service.AllowableRangeValues; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin; import springfox.documentation.spi.schema.contexts.ModelPropertyContext; @Component public class Jsr303ModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin { @Override public boolean supports(DocumentationType delimiter) { return true; } @Override public void apply(ModelPropertyContext context) { ModelPropertyBuilder builder = context.getBuilder(); // プロパティのgetterを取得する Optional<BeanPropertyDefinition> beanPropDef = context.getBeanPropertyDefinition(); BeanPropertyDefinition beanDef = beanPropDef.get(); AnnotatedMethod method = beanDef.getGetter(); if (method == null) { return; } // 必須・非必須を取得する NotNull notNull = method.getAnnotation(NotNull.class); NotEmpty notEmpty = method.getAnnotation(NotEmpty.class); if (notNull != null || notEmpty != null) { builder.required(true); } // 範囲制約を取得する Range range = method.getAnnotation(Range.class); if (range != null) { builder.allowableValues(new AllowableRangeValues( Long.toString(range.min()), Long.toString(range.max()))); } Size size = method.getAnnotation(Size.class); if (size != null) { builder.allowableValues(new AllowableRangeValues( Long.toString(size.min()), Long.toString(size.max()))); } } }
Jsr303ModelPropertyBuilderPluginクラスのapplyメソッドで、各プロパティのgetterについているアノテーションを取得し、その内容に応じてModelPropertyBuilderに制約条件を設定しています。
#「Jsr303」というクラス名にしていますが、Hibernate Validatorのアノテーションも処理対象に加えています^^;
上記クラスを作成しておけば、次のように、Employeeクラスの@ApiModelPropertyから制約に関連する属性(requiredとallowableValues)を削除し、冗長な記述を排除できます。
(getterのみ抜粋して記載)
@ApiModelProperty(value = "Employee ID.") @Range(min = 1, max = 100) public Integer getId() { return this.id; } @ApiModelProperty(value = "Employee's name.") @NotEmpty @Size(max = 32) public String getName() { return this.name; } @ApiModelProperty(value = "Employee's birthday with ISO 8601 format.") @NotNull public Date getBirthday() { return this.birthday; }
これらを実施した上で再度SpringBootアプリケーションを起動すると、先ほどと同じSwagger UIページが生成されます。
必要に応じて対応アノテーションを増やす必要はありますが、冗長な記述は排除できました。
bootprint-swaggerで静的ドキュメント生成!
ということで、前回同様、bootprint-swaggerでHTMLのAPI仕様書を生成してみます。
なかなか難しい表現が出てきました・・・(汗)
{ x ∈ Z | 1 ≤ x ≤ 100 } のZは、代数学で言うところの「整数の集合」を表しています。
あと、nameのところは (up to chars) と出力され、具体的な文字列長が出てきませんでした。
ちょっとイマイチ感がありますね。。
Bootprint側のカスタマイズは調べてみようと思います。
それでは。
Acroquest Technologyでは、キャリア採用を行っています。
- 日頃勉強している成果を、AWS、Hadoop、Storm、NoSQL、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
- 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
- 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
- OSSの開発に携わりたい。
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
競合は海外・外資!国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の求人 - Wantedly