4 changed files with 327 additions and 3 deletions
@ -0,0 +1,190 @@
@@ -0,0 +1,190 @@
|
||||
/* |
||||
* Copyright 2002-2016 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.reactive.result.method.annotation; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView; |
||||
import static org.junit.Assert.assertEquals; |
||||
import org.junit.Test; |
||||
import reactor.core.publisher.Flux; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.annotation.ComponentScan; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.reactive.config.WebReactiveConfiguration; |
||||
|
||||
/** |
||||
* @author Sebastien Deleuze |
||||
*/ |
||||
public class JacksonHintsIntegrationTests extends AbstractRequestMappingIntegrationTests { |
||||
|
||||
@Override |
||||
protected ApplicationContext initApplicationContext() { |
||||
AnnotationConfigApplicationContext wac = new AnnotationConfigApplicationContext(); |
||||
wac.register(WebConfig.class); |
||||
wac.refresh(); |
||||
return wac; |
||||
} |
||||
|
||||
@Test |
||||
public void jsonViewResponse() throws Exception { |
||||
String expected = "{\"withView1\":\"with\"}"; |
||||
assertEquals(expected, performGet("/response/raw", MediaType.APPLICATION_JSON_UTF8, String.class).getBody()); |
||||
} |
||||
|
||||
@Test |
||||
public void jsonViewWithMonoResponse() throws Exception { |
||||
String expected = "{\"withView1\":\"with\"}"; |
||||
assertEquals(expected, performGet("/response/mono", MediaType.APPLICATION_JSON_UTF8, String.class).getBody()); |
||||
} |
||||
|
||||
@Test |
||||
public void jsonViewWithFluxResponse() throws Exception { |
||||
String expected = "[{\"withView1\":\"with\"},{\"withView1\":\"with\"}]"; |
||||
assertEquals(expected, performGet("/response/flux", MediaType.APPLICATION_JSON_UTF8, String.class).getBody()); |
||||
} |
||||
|
||||
@Test |
||||
public void jsonViewWithRequest() throws Exception { |
||||
String expected = "{\"withView1\":\"with\",\"withView2\":null,\"withoutView\":null}"; |
||||
assertEquals(expected, performPost("/request/raw", MediaType.APPLICATION_JSON, |
||||
new JacksonViewBean("with", "with", "without"), MediaType.APPLICATION_JSON_UTF8, String.class).getBody()); |
||||
} |
||||
|
||||
@Test |
||||
public void jsonViewWithMonoRequest() throws Exception { |
||||
String expected = "{\"withView1\":\"with\",\"withView2\":null,\"withoutView\":null}"; |
||||
assertEquals(expected, performPost("/request/mono", MediaType.APPLICATION_JSON, |
||||
new JacksonViewBean("with", "with", "without"), MediaType.APPLICATION_JSON_UTF8, String.class).getBody()); |
||||
} |
||||
|
||||
@Test |
||||
public void jsonViewWithFluxRequest() throws Exception { |
||||
String expected = "[{\"withView1\":\"with\",\"withView2\":null,\"withoutView\":null}," + |
||||
"{\"withView1\":\"with\",\"withView2\":null,\"withoutView\":null}]"; |
||||
List<JacksonViewBean> beans = Arrays.asList(new JacksonViewBean("with", "with", "without"), new JacksonViewBean("with", "with", "without")); |
||||
assertEquals(expected, performPost("/request/flux", MediaType.APPLICATION_JSON, beans, |
||||
MediaType.APPLICATION_JSON_UTF8, String.class).getBody()); |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
@ComponentScan(resourcePattern = "**/JacksonHintsIntegrationTests*.class") |
||||
@SuppressWarnings({"unused", "WeakerAccess"}) |
||||
static class WebConfig extends WebReactiveConfiguration { |
||||
} |
||||
|
||||
@RestController |
||||
@SuppressWarnings("unused") |
||||
private static class JsonViewRestController { |
||||
|
||||
@GetMapping("/response/raw") |
||||
@JsonView(MyJacksonView1.class) |
||||
public JacksonViewBean rawResponse() { |
||||
return new JacksonViewBean("with", "with", "without"); |
||||
} |
||||
|
||||
@GetMapping("/response/mono") |
||||
@JsonView(MyJacksonView1.class) |
||||
public Mono<JacksonViewBean> monoResponse() { |
||||
return Mono.just(new JacksonViewBean("with", "with", "without")); |
||||
} |
||||
|
||||
@GetMapping("/response/flux") |
||||
@JsonView(MyJacksonView1.class) |
||||
public Flux<JacksonViewBean> fluxResponse() { |
||||
return Flux.just(new JacksonViewBean("with", "with", "without"), new JacksonViewBean("with", "with", "without")); |
||||
} |
||||
|
||||
@PostMapping("/request/raw") |
||||
public JacksonViewBean rawRequest(@JsonView(MyJacksonView1.class) @RequestBody JacksonViewBean bean) { |
||||
return bean; |
||||
} |
||||
|
||||
@PostMapping("/request/mono") |
||||
public Mono<JacksonViewBean> monoRequest(@JsonView(MyJacksonView1.class) @RequestBody Mono<JacksonViewBean> mono) { |
||||
return mono; |
||||
} |
||||
|
||||
@PostMapping("/request/flux") |
||||
public Flux<JacksonViewBean> fluxRequest(@JsonView(MyJacksonView1.class) @RequestBody Flux<JacksonViewBean> flux) { |
||||
return flux; |
||||
} |
||||
|
||||
} |
||||
|
||||
private interface MyJacksonView1 {} |
||||
|
||||
private interface MyJacksonView2 {} |
||||
|
||||
|
||||
@SuppressWarnings("unused") |
||||
private static class JacksonViewBean { |
||||
|
||||
@JsonView(MyJacksonView1.class) |
||||
private String withView1; |
||||
|
||||
@JsonView(MyJacksonView2.class) |
||||
private String withView2; |
||||
|
||||
private String withoutView; |
||||
|
||||
|
||||
public JacksonViewBean() { |
||||
} |
||||
|
||||
public JacksonViewBean(String withView1, String withView2, String withoutView) { |
||||
this.withView1 = withView1; |
||||
this.withView2 = withView2; |
||||
this.withoutView = withoutView; |
||||
} |
||||
|
||||
public String getWithView1() { |
||||
return withView1; |
||||
} |
||||
|
||||
public void setWithView1(String withView1) { |
||||
this.withView1 = withView1; |
||||
} |
||||
|
||||
public String getWithView2() { |
||||
return withView2; |
||||
} |
||||
|
||||
public void setWithView2(String withView2) { |
||||
this.withView2 = withView2; |
||||
} |
||||
|
||||
public String getWithoutView() { |
||||
return withoutView; |
||||
} |
||||
|
||||
public void setWithoutView(String withoutView) { |
||||
this.withoutView = withoutView; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 2002-2016 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.codec; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView; |
||||
|
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.codec.json.AbstractJackson2Codec; |
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
|
||||
/** |
||||
* {@link ServerHttpMessageReader} that resolves those annotation or request based Jackson 2 hints: |
||||
* <ul> |
||||
* <li>{@code @JsonView} + {@code @RequestBody} annotated handler method parameter</li> |
||||
* </ul> |
||||
* |
||||
* @author Sebastien Deleuze |
||||
* @since 5.0 |
||||
* @see com.fasterxml.jackson.annotation.JsonView |
||||
*/ |
||||
public class Jackson2ServerHttpMessageReader extends AbstractServerHttpMessageReader<Object> { |
||||
|
||||
public Jackson2ServerHttpMessageReader(HttpMessageReader<Object> reader) { |
||||
super(reader); |
||||
} |
||||
|
||||
@Override |
||||
protected Map<String, Object> resolveReadHintsInternal(ResolvableType streamType, |
||||
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request) { |
||||
|
||||
Object source = streamType.getSource(); |
||||
MethodParameter parameter = (source instanceof MethodParameter ? (MethodParameter)source : null); |
||||
if (parameter != null) { |
||||
JsonView annotation = parameter.getParameterAnnotation(JsonView.class); |
||||
if (annotation != null) { |
||||
Class<?>[] classes = annotation.value(); |
||||
if (classes.length != 1) { |
||||
throw new IllegalArgumentException( |
||||
"@JsonView only supported for read hints with exactly 1 class argument: " + parameter); |
||||
} |
||||
return Collections.singletonMap(AbstractJackson2Codec.JSON_VIEW_HINT, classes[0]); |
||||
} |
||||
} |
||||
return Collections.emptyMap(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 2002-2016 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.http.codec; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView; |
||||
|
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.codec.json.AbstractJackson2Codec; |
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
|
||||
/** |
||||
* {@link ServerHttpMessageWriter} that resolves those annotation or request based Jackson 2 hints: |
||||
* <ul> |
||||
* <li>{@code @JsonView} annotated handler method</li> |
||||
* </ul> |
||||
* |
||||
* @author Sebastien Deleuze |
||||
* @since 5.0 |
||||
* @see com.fasterxml.jackson.annotation.JsonView |
||||
*/ |
||||
public class Jackson2ServerHttpMessageWriter extends AbstractServerHttpMessageWriter<Object> { |
||||
|
||||
public Jackson2ServerHttpMessageWriter(HttpMessageWriter<Object> writer) { |
||||
super(writer); |
||||
} |
||||
|
||||
@Override |
||||
protected Map<String, Object> resolveWriteHintsInternal(ResolvableType streamType, |
||||
ResolvableType elementType, MediaType mediaType, ServerHttpRequest request) { |
||||
|
||||
Object source = streamType.getSource(); |
||||
MethodParameter returnValue = (source instanceof MethodParameter ? (MethodParameter)source : null); |
||||
if (returnValue != null) { |
||||
JsonView annotation = returnValue.getMethodAnnotation(JsonView.class); |
||||
if (annotation != null) { |
||||
Class<?>[] classes = annotation.value(); |
||||
if (classes.length != 1) { |
||||
throw new IllegalArgumentException( |
||||
"@JsonView only supported for write hints with exactly 1 class argument: " + returnValue); |
||||
} |
||||
return Collections.singletonMap(AbstractJackson2Codec.JSON_VIEW_HINT, classes[0]); |
||||
} |
||||
} |
||||
return Collections.emptyMap(); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue