Allow an operation to specify the media types that it produces
Closes gh-10118
This commit is contained in:
parent
80f023f996
commit
f49741e3ed
|
|
@ -26,6 +26,7 @@ import java.lang.annotation.Target;
|
|||
* Identifies a method on an {@link Endpoint} as being a delete operation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
|
|
@ -33,4 +34,11 @@ import java.lang.annotation.Target;
|
|||
@Documented
|
||||
public @interface DeleteOperation {
|
||||
|
||||
/**
|
||||
* The media type of the result of the operation.
|
||||
*
|
||||
* @return the media type
|
||||
*/
|
||||
String[] produces() default {};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,11 @@ import java.lang.annotation.Target;
|
|||
@Documented
|
||||
public @interface ReadOperation {
|
||||
|
||||
/**
|
||||
* The media type of the result of the operation.
|
||||
*
|
||||
* @return the media type
|
||||
*/
|
||||
String[] produces() default {};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,11 @@ import java.lang.annotation.Target;
|
|||
@Documented
|
||||
public @interface WriteOperation {
|
||||
|
||||
/**
|
||||
* The media type of the result of the operation.
|
||||
*
|
||||
* @return the media type
|
||||
*/
|
||||
String[] produces() default {};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.endpoint.web;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -134,7 +135,8 @@ public class WebAnnotationEndpointDiscoverer extends
|
|||
OperationRequestPredicate requestPredicate = new OperationRequestPredicate(
|
||||
determinePath(endpointId, method), httpMethod,
|
||||
determineConsumedMediaTypes(httpMethod, method),
|
||||
determineProducedMediaTypes(method));
|
||||
determineProducedMediaTypes(
|
||||
operationAttributes.getStringArray("produces"), method));
|
||||
OperationInvoker invoker = new ReflectiveOperationInvoker(
|
||||
this.parameterMapper, target, method);
|
||||
if (timeToLive > 0) {
|
||||
|
|
@ -171,7 +173,11 @@ public class WebAnnotationEndpointDiscoverer extends
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private Collection<String> determineProducedMediaTypes(Method method) {
|
||||
private Collection<String> determineProducedMediaTypes(String[] produces,
|
||||
Method method) {
|
||||
if (produces.length > 0) {
|
||||
return Arrays.asList(produces);
|
||||
}
|
||||
if (Void.class.equals(method.getReturnType())
|
||||
|| void.class.equals(method.getReturnType())) {
|
||||
return Collections.emptyList();
|
||||
|
|
|
|||
|
|
@ -254,6 +254,14 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
|
|||
.isEqualTo("alpha"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readOperationWithCustomMediaType() {
|
||||
load(CustomMediaTypesEndpointConfiguration.class,
|
||||
(client) -> client.get().uri("/custommediatypes").exchange()
|
||||
.expectStatus().isOk().expectHeader()
|
||||
.valueMatches("Content-Type", "text/plain(;charset=.*)?"));
|
||||
}
|
||||
|
||||
protected abstract T createApplicationContext(Class<?>... config);
|
||||
|
||||
protected abstract int getPort(T context);
|
||||
|
|
@ -422,6 +430,17 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseConfiguration.class)
|
||||
static class CustomMediaTypesEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public CustomMediaTypesEndpoint customMediaTypesEndpoint() {
|
||||
return new CustomMediaTypesEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Endpoint(id = "test")
|
||||
static class TestEndpoint {
|
||||
|
||||
|
|
@ -580,6 +599,16 @@ public abstract class AbstractWebEndpointIntegrationTests<T extends Configurable
|
|||
|
||||
}
|
||||
|
||||
@Endpoint(id = "custommediatypes")
|
||||
static class CustomMediaTypesEndpoint {
|
||||
|
||||
@ReadOperation(produces = "text/plain")
|
||||
public String read() {
|
||||
return "read";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface EndpointDelegate {
|
||||
|
||||
void write();
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.junit.rules.ExpectedException;
|
|||
import org.springframework.boot.endpoint.CachingConfiguration;
|
||||
import org.springframework.boot.endpoint.CachingOperationInvoker;
|
||||
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
|
||||
import org.springframework.boot.endpoint.DeleteOperation;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.EndpointExposure;
|
||||
import org.springframework.boot.endpoint.EndpointInfo;
|
||||
|
|
@ -216,6 +217,24 @@ public class WebAnnotationEndpointDiscovererTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void operationCanProduceCustomMediaTypes() {
|
||||
load(CustomMediaTypesEndpointConfiguration.class, (discoverer) -> {
|
||||
Map<String, EndpointInfo<WebEndpointOperation>> endpoints = mapEndpoints(
|
||||
discoverer.discoverEndpoints());
|
||||
assertThat(endpoints).containsOnlyKeys("custommediatypes");
|
||||
EndpointInfo<WebEndpointOperation> endpoint = endpoints
|
||||
.get("custommediatypes");
|
||||
assertThat(requestPredicates(endpoint)).has(requestPredicates(
|
||||
path("custommediatypes").httpMethod(WebEndpointHttpMethod.GET)
|
||||
.consumes().produces("text/plain"),
|
||||
path("custommediatypes").httpMethod(WebEndpointHttpMethod.POST)
|
||||
.consumes().produces("a/b", "c/d"),
|
||||
path("custommediatypes").httpMethod(WebEndpointHttpMethod.DELETE)
|
||||
.consumes().produces("text/plain")));
|
||||
});
|
||||
}
|
||||
|
||||
private void load(Class<?> configuration,
|
||||
Consumer<WebAnnotationEndpointDiscoverer> consumer) {
|
||||
this.load((id) -> null, configuration, consumer);
|
||||
|
|
@ -415,6 +434,27 @@ public class WebAnnotationEndpointDiscovererTests {
|
|||
|
||||
}
|
||||
|
||||
@Endpoint(id = "custommediatypes")
|
||||
static class CustomMediaTypesEndpoint {
|
||||
|
||||
@ReadOperation(produces = "text/plain")
|
||||
public String read() {
|
||||
return "read";
|
||||
}
|
||||
|
||||
@WriteOperation(produces = { "a/b", "c/d" })
|
||||
public String write() {
|
||||
return "write";
|
||||
|
||||
}
|
||||
|
||||
@DeleteOperation(produces = "text/plain")
|
||||
public String delete() {
|
||||
return "delete";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class MultipleEndpointsConfiguration {
|
||||
|
||||
|
|
@ -578,6 +618,17 @@ public class WebAnnotationEndpointDiscovererTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseConfiguration.class)
|
||||
static class CustomMediaTypesEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public CustomMediaTypesEndpoint customMediaTypesEndpoint() {
|
||||
return new CustomMediaTypesEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class RequestPredicateMatcher {
|
||||
|
||||
private final String path;
|
||||
|
|
|
|||
Loading…
Reference in New Issue