なんでデフォルトの挙動がこんな事になってるんですかね・・・
現象
以下はチュートリアルのjsonを返すコントローラーです。
import java.util.concurrent.atomic.AtomicLong; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.music.anime.tree.json.Greeting; @RestController public class GreetingController { private static final String template = "Hello, %s!"; private final AtomicLong counter = new AtomicLong(); @RequestMapping(value = "/greeting", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) { return new Greeting(counter.incrementAndGet(), String.format(template, name)); } }
しかし、jsonではクロスドメインの壁を超えられないので、jsonpを返したいところです。
そこで以下のようなクラスとメソッドを用意すると、親クラスのjsonpQueryParamNamesに「callback」というパラメータが設定されます。この値が設定されると「callback」というパラメータがある場合はjsonpになるよ、という処理がされます。
import java.io.IOException; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJacksonValue; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; @ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } }
しかしこの状態でjsonpを出力してみると、何故か以下のようなjsonpが返ってしまいます。
http://localhost:8080/greeting?callback=test
/**/test({"id":1,"content":"Hello, World!"});
何故か先頭に「/**/」というコメントが挿入されてしまいます。
正直これではカッコ悪過ぎますね。
対応
何故先頭に謎ものコメントが挿入されるのかあまり解ってないのですが、以下のようなメソッドを定義しておくと、謎の「/**/」を削除する事ができます。
import java.io.IOException; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJacksonValue; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractJsonpResponseBodyAdvice; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; @ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } @Bean public MappingJackson2HttpMessageConverter MappingJackson2HttpMessageConverter(ApplicationContext ctx) { ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().applicationContext(ctx).build(); return new MappingJackson2HttpMessageConverter(mapper) { @Override protected void writePrefix(JsonGenerator generator, Object obj) throws IOException { if (!(obj instanceof MappingJacksonValue)) return; String funcName = ((MappingJacksonValue) obj).getJsonpFunction(); if (funcName != null) generator.writeRaw(funcName + "("); } }; } }
MappingJackson2HttpMessageConverterメソッドがあると、
http://localhost:8080/greeting?callback=test
のURLで以下の正常なjsonpが返るようになります。
test({"id":1,"content":"Hello, World!"});
これでようやくまともなjsonpを生成する事ができます。