2021/03/12 このエントリーをはてなブックマークに追加 はてなブックマーク - [Spring]WebMvcConfigurerでコントローラーで受け取る値を好きに変換する

[Spring]WebMvcConfigurerでコントローラーで受け取る値を好きに変換する

カテゴリ: ,

ただのメモ。


PathVariableとかを任意の型で受け取りたいんだー。
例えば、以下のFugaTypeのような感じ。


Kotlin
    @GetMapping("hoge/{fuga}")
    fun Hoge(
        @PathVariable fuga: FugaType
    ): HogeResponse {
        return service.Hoge()
    }

EnumはStringToEnumConverterFactoryにより、Enum.valueOfで変換可能であればStringからEnumへの変換を勝手にしてくれます。


Enum.valueOfに依存するということは、
enum FugaType { fuga, hoge; } 
みたいに小文字の命名で列挙しないとマッピングできない(すごい些細な話ではある)。
強いて言うなら、
enum FugaType { FUGA, HOGE; } 
って書けたら嬉しいなーとか。
もうちょっと自由にEnum変換をかけたいよねって時もある(ない?)
あと日付型とかをよしなに扱うために早いタイミングで変換かけたいとか。ありそう(ない?)


SpringもSpring Bootもなんもわからんのですが、
WebMvcConfigurerを使うとカスタムコンバーターを登録できるようです。
1. Converterを登録する、2. ConverterFactoryを登録する、の2択のようです。
addFormattersをオーバーライドして、FormatterRegistryに対してaddConverterかaddConverterFactoryのいずれかを使えば良いです。
WebMvcConfigurerはdefault実装を持つinterfaceなので、必要なものだけoverrideすれば良いらしい(Springの5.x以降はそうなっている)。以前まで抽象実装を持ったAdapterを使う必要があったっぽい。


Kotlin

import com.hoge.fuga.StringToFugaTypeConverter
import com.hoge.fuga.StringToHogeTypeConverterFactory
import org.springframework.context.annotation.Configuration
import org.springframework.format.FormatterRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
class WebConfig : WebMvcConfigurer {
    override fun addFormatters(registry: FormatterRegistry) {
        registry.addConverter(StringToFugaTypeConverter())
        registry.addConverterFactory(StringToHogeTypeConverterFactory())

    }
}


Converterを使う場合は、当然ですがConverterインターフェースをimplementsした具象実装が必要です。

こんな感じでインターフェース実装を作って前述のFormatterRegistryに登録してあげます。
Kotlin
class StringToFugaConverter : Converter<String, FugaType> {
        override fun convert(source: String): FugaType {
            return TODO("Stringを受け取ってFugaを返す")
        }
}

同様にConverterFactory方式にする場合も、インターフェース実装を作る必要があります。
こちらもお好みでいい感じに作ってFormatterRegistryに登録します。

StringToEnumConverterFactoryがSpringに標準で入っているっぽいので参考としては良い気がします。
こんな感じ


Java
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.lang.Nullable;

final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

	@Override
	public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
		return new StringToEnum(ConversionUtils.getEnumType(targetType));
	}


	private static class StringToEnum<T extends Enum> implements Converter<String, T> {

		private final Class<T> enumType;

		StringToEnum(Class<T> enumType) {
			this.enumType = enumType;
		}

		@Override
		@Nullable
		public T convert(String source) {
			if (source.isEmpty()) {
				// It's an empty enum identifier: reset the enum value to null.
				return null;
			}
			return (T) Enum.valueOf(this.enumType, source.trim());
		}
	}

}



これを参考に、StringToHogeTypeConverterFactoryみたいなものを書いて登録すれば良さそうですね。



たぶんConverterFactoryの方が、入力ごとにConverter使い分けるなどFactory側で調整できるというのがありそうですね。




はい、これでお好みで型変換が出来ますね。めでたし。





https://spring.pleiades.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-auto-configuration
https://www.ne.jp/asahi/hishidama/home/tech/java/enum.html
https://www.baeldung.com/spring-type-conversions
https://qiita.com/yushi_koga/items/ff3338c34ce59c20bd5f



0 件のコメント:

コメントを投稿

GA