mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Support "application/graphql+json" media type in GraphQL HTTP mapping
As seen in spring-projects/spring-graphql#108, the GraphQL HTTP spec now requires the "application/graphql+json" media type and accepts "application/json" for backwards compatibility. This commit updates the `RouterFunction` definition for the GraphQL HTTP endpoints so that both types are accepted. Closes gh-30407
This commit is contained in:
parent
d476d8e37b
commit
1c71567c94
@ -81,8 +81,8 @@ import static org.springframework.web.reactive.function.server.RequestPredicates
|
|||||||
@EnableConfigurationProperties(GraphQlCorsProperties.class)
|
@EnableConfigurationProperties(GraphQlCorsProperties.class)
|
||||||
public class GraphQlWebFluxAutoConfiguration {
|
public class GraphQlWebFluxAutoConfiguration {
|
||||||
|
|
||||||
private static final RequestPredicate ACCEPT_JSON_CONTENT = accept(MediaType.APPLICATION_JSON)
|
private static final RequestPredicate SUPPORTS_MEDIATYPES = accept(MediaType.APPLICATION_GRAPHQL,
|
||||||
.and(contentType(MediaType.APPLICATION_JSON));
|
MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_GRAPHQL, MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(GraphQlWebFluxAutoConfiguration.class);
|
private static final Log logger = LogFactory.getLog(GraphQlWebFluxAutoConfiguration.class);
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ public class GraphQlWebFluxAutoConfiguration {
|
|||||||
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
|
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
|
||||||
RouterFunctions.Builder builder = RouterFunctions.route();
|
RouterFunctions.Builder builder = RouterFunctions.route();
|
||||||
builder = builder.GET(path, this::onlyAllowPost);
|
builder = builder.GET(path, this::onlyAllowPost);
|
||||||
builder = builder.POST(path, ACCEPT_JSON_CONTENT, httpHandler::handleRequest);
|
builder = builder.POST(path, SUPPORTS_MEDIATYPES, httpHandler::handleRequest);
|
||||||
if (properties.getGraphiql().isEnabled()) {
|
if (properties.getGraphiql().isEnabled()) {
|
||||||
GraphiQlHandler graphQlHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
GraphiQlHandler graphQlHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
||||||
builder = builder.GET(properties.getGraphiql().getPath(), graphQlHandler::handleRequest);
|
builder = builder.GET(properties.getGraphiql().getPath(), graphQlHandler::handleRequest);
|
||||||
|
@ -87,7 +87,7 @@ public class GraphQlWebMvcAutoConfiguration {
|
|||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(GraphQlWebMvcAutoConfiguration.class);
|
private static final Log logger = LogFactory.getLog(GraphQlWebMvcAutoConfiguration.class);
|
||||||
|
|
||||||
private static MediaType[] SUPPORTED_MEDIA_TYPES = new MediaType[] { MediaType.valueOf("application/graphql+json"),
|
private static MediaType[] SUPPORTED_MEDIA_TYPES = new MediaType[] { MediaType.APPLICATION_GRAPHQL,
|
||||||
MediaType.APPLICATION_JSON };
|
MediaType.APPLICATION_JSON };
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ -113,8 +113,8 @@ public class GraphQlWebMvcAutoConfiguration {
|
|||||||
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
|
logger.info(LogMessage.format("GraphQL endpoint HTTP POST %s", path));
|
||||||
RouterFunctions.Builder builder = RouterFunctions.route();
|
RouterFunctions.Builder builder = RouterFunctions.route();
|
||||||
builder = builder.GET(path, this::onlyAllowPost);
|
builder = builder.GET(path, this::onlyAllowPost);
|
||||||
builder = builder.POST(path, RequestPredicates.contentType(MediaType.APPLICATION_JSON)
|
builder = builder.POST(path, RequestPredicates.contentType(SUPPORTED_MEDIA_TYPES)
|
||||||
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), httpHandler::handleRequest);
|
.and(RequestPredicates.accept(SUPPORTED_MEDIA_TYPES)), httpHandler::handleRequest);
|
||||||
if (properties.getGraphiql().isEnabled()) {
|
if (properties.getGraphiql().isEnabled()) {
|
||||||
GraphiQlHandler graphiQLHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
GraphiQlHandler graphiQLHandler = new GraphiQlHandler(path, properties.getWebsocket().getPath());
|
||||||
builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::handleRequest);
|
builder = builder.GET(properties.getGraphiql().getPath(), graphiQLHandler::handleRequest);
|
||||||
|
@ -75,7 +75,8 @@ class GraphQlWebFluxAutoConfigurationTests {
|
|||||||
testWithWebClient((client) -> {
|
testWithWebClient((client) -> {
|
||||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||||
client.post().uri("/graphql").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
client.post().uri("/graphql").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
||||||
.expectBody().jsonPath("data.bookById.name").isEqualTo("GraphQL for beginners");
|
.expectHeader().contentType("application/graphql+json").expectBody().jsonPath("data.bookById.name")
|
||||||
|
.isEqualTo("GraphQL for beginners");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,8 +151,8 @@ class GraphQlWebFluxAutoConfigurationTests {
|
|||||||
this.contextRunner.run((context) -> {
|
this.contextRunner.run((context) -> {
|
||||||
WebTestClient client = WebTestClient.bindToApplicationContext(context).configureClient()
|
WebTestClient client = WebTestClient.bindToApplicationContext(context).configureClient()
|
||||||
.defaultHeaders((headers) -> {
|
.defaultHeaders((headers) -> {
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_GRAPHQL);
|
||||||
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
|
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_GRAPHQL));
|
||||||
}).baseUrl(BASE_URL).build();
|
}).baseUrl(BASE_URL).build();
|
||||||
consumer.accept(client);
|
consumer.accept(client);
|
||||||
});
|
});
|
||||||
|
@ -81,7 +81,7 @@ class GraphQlWebMvcAutoConfigurationTests {
|
|||||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||||
MvcResult result = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")).andReturn();
|
MvcResult result = mockMvc.perform(post("/graphql").content("{\"query\": \"" + query + "\"}")).andReturn();
|
||||||
mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk())
|
mockMvc.perform(asyncDispatch(result)).andExpect(status().isOk())
|
||||||
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
|
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_GRAPHQL))
|
||||||
.andExpect(jsonPath("data.bookById.name").value("GraphQL for beginners"));
|
.andExpect(jsonPath("data.bookById.name").value("GraphQL for beginners"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -155,9 +155,9 @@ class GraphQlWebMvcAutoConfigurationTests {
|
|||||||
|
|
||||||
private void testWith(MockMvcConsumer mockMvcConsumer) {
|
private void testWith(MockMvcConsumer mockMvcConsumer) {
|
||||||
this.contextRunner.run((context) -> {
|
this.contextRunner.run((context) -> {
|
||||||
MediaType mediaType = MediaType.APPLICATION_JSON;
|
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).defaultRequest(
|
||||||
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
post("/graphql").contentType(MediaType.APPLICATION_GRAPHQL).accept(MediaType.APPLICATION_GRAPHQL))
|
||||||
.defaultRequest(post("/graphql").contentType(mediaType).accept(mediaType)).build();
|
.build();
|
||||||
mockMvcConsumer.accept(mockMvc);
|
mockMvcConsumer.accept(mockMvc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ class HttpGraphQlTesterContextCustomizerIntegrationTests {
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||||
response.setStatusCode(HttpStatus.OK);
|
response.setStatusCode(HttpStatus.OK);
|
||||||
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
response.getHeaders().setContentType(MediaType.APPLICATION_GRAPHQL);
|
||||||
return response.writeWith(Mono.just(factory.wrap("{\"data\":{}}".getBytes())));
|
return response.writeWith(Mono.just(factory.wrap("{\"data\":{}}".getBytes())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ class HttpGraphQlTesterContextCustomizerWithCustomBasePathTests {
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||||
response.setStatusCode(HttpStatus.OK);
|
response.setStatusCode(HttpStatus.OK);
|
||||||
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
response.getHeaders().setContentType(MediaType.APPLICATION_GRAPHQL);
|
||||||
return response.writeWith(Mono.just(factory.wrap("{\"data\":{}}".getBytes())));
|
return response.writeWith(Mono.just(factory.wrap("{\"data\":{}}".getBytes())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class HttpGraphQlTesterContextCustomizerWithCustomContextPathTests {
|
|||||||
@RestController
|
@RestController
|
||||||
static class TestController {
|
static class TestController {
|
||||||
|
|
||||||
@PostMapping(path = "/graphql", produces = MediaType.APPLICATION_JSON_VALUE)
|
@PostMapping(path = "/graphql", produces = MediaType.APPLICATION_GRAPHQL_VALUE)
|
||||||
String graphql() {
|
String graphql() {
|
||||||
return "{}";
|
return "{}";
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user