Skip to content

Commit 21a7c81

Browse files
committed
Move handleRequest up to AbstractGraphQlHttpHandler
Closes gh-959
1 parent 6376ec9 commit 21a7c81

File tree

6 files changed

+215
-195
lines changed

6 files changed

+215
-195
lines changed

spring-graphql/src/main/java/org/springframework/graphql/server/webflux/AbstractGraphQlHttpHandler.java

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,85 @@
1717
package org.springframework.graphql.server.webflux;
1818

1919

20+
import java.util.List;
21+
22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
2024
import reactor.core.publisher.Mono;
2125

2226
import org.springframework.core.io.buffer.DataBuffer;
27+
import org.springframework.graphql.ResponseError;
2328
import org.springframework.graphql.server.WebGraphQlHandler;
29+
import org.springframework.graphql.server.WebGraphQlRequest;
30+
import org.springframework.graphql.server.WebGraphQlResponse;
2431
import org.springframework.graphql.server.support.SerializableGraphQlRequest;
2532
import org.springframework.http.HttpHeaders;
2633
import org.springframework.http.MediaType;
34+
import org.springframework.http.codec.CodecConfigurer;
2735
import org.springframework.lang.Nullable;
2836
import org.springframework.util.Assert;
37+
import org.springframework.util.CollectionUtils;
2938
import org.springframework.web.reactive.function.server.ServerRequest;
39+
import org.springframework.web.reactive.function.server.ServerResponse;
3040
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
3141

3242
/**
33-
* Abstract class for GraphQL Handler implementations using the HTTP transport.
43+
* Abstract base class for GraphQL over HTTP handlers.
3444
*
3545
* @author Brian Clozel
3646
* @author Rossen Stoyanchev
47+
* @since 1.3.0
3748
*/
38-
class AbstractGraphQlHttpHandler {
49+
public abstract class AbstractGraphQlHttpHandler {
50+
51+
protected final Log logger = LogFactory.getLog(getClass());
52+
3953

40-
protected final WebGraphQlHandler graphQlHandler;
54+
private final WebGraphQlHandler graphQlHandler;
4155

4256
@Nullable
43-
protected final HttpCodecDelegate codecDelegate;
57+
private final HttpCodecDelegate codecDelegate;
58+
59+
60+
protected AbstractGraphQlHttpHandler(
61+
WebGraphQlHandler graphQlHandler, @Nullable CodecConfigurer codecConfigurer) {
4462

45-
AbstractGraphQlHttpHandler(WebGraphQlHandler graphQlHandler, @Nullable HttpCodecDelegate codecDelegate) {
4663
Assert.notNull(graphQlHandler, "WebGraphQlHandler is required");
4764
this.graphQlHandler = graphQlHandler;
48-
this.codecDelegate = codecDelegate;
65+
this.codecDelegate = (codecConfigurer != null) ? new HttpCodecDelegate(codecConfigurer) : null;
4966
}
5067

51-
protected Mono<SerializableGraphQlRequest> readRequest(ServerRequest serverRequest) {
68+
69+
/**
70+
* Handle GraphQL over HTTP request.
71+
* @param request the current request
72+
* @return the resulting response
73+
*/
74+
public Mono<ServerResponse> handleRequest(ServerRequest request) {
75+
return readRequest(request)
76+
.flatMap((body) -> {
77+
WebGraphQlRequest graphQlRequest = new WebGraphQlRequest(
78+
request.uri(), request.headers().asHttpHeaders(),
79+
request.cookies(), request.remoteAddress().orElse(null),
80+
request.attributes(), body,
81+
request.exchange().getRequest().getId(),
82+
request.exchange().getLocaleContext().getLocale());
83+
if (this.logger.isDebugEnabled()) {
84+
this.logger.debug("Executing: " + graphQlRequest);
85+
}
86+
return this.graphQlHandler.handleRequest(graphQlRequest);
87+
})
88+
.flatMap((response) -> {
89+
if (this.logger.isDebugEnabled()) {
90+
List<ResponseError> errors = response.getErrors();
91+
this.logger.debug("Execution result " +
92+
(!CollectionUtils.isEmpty(errors) ? "has errors: " + errors : "is ready") + ".");
93+
}
94+
return prepareResponse(request, response);
95+
});
96+
}
97+
98+
private Mono<SerializableGraphQlRequest> readRequest(ServerRequest serverRequest) {
5299
if (this.codecDelegate != null) {
53100
MediaType contentType = serverRequest.headers().contentType().orElse(MediaType.APPLICATION_JSON);
54101
return this.codecDelegate.decode(serverRequest.bodyToFlux(DataBuffer.class), contentType);
@@ -75,5 +122,22 @@ private static Mono<SerializableGraphQlRequest> applyApplicationGraphQlFallback(
75122
Mono.error(ex);
76123
}
77124

125+
/**
126+
* Prepare the {@link ServerResponse} for the given GraphQL response.
127+
* @param request the current request
128+
* @param response the GraphQL response
129+
* @return the server response
130+
*/
131+
protected abstract Mono<ServerResponse> prepareResponse(ServerRequest request, WebGraphQlResponse response);
132+
133+
/**
134+
* Encode the GraphQL response if custom codecs were provided, or otherwise
135+
* return the result map.
136+
* @param response the GraphQL response
137+
* @return the encoded response or the result map
138+
*/
139+
protected Object encodeResponseIfNecessary(WebGraphQlResponse response) {
140+
return (this.codecDelegate != null) ? this.codecDelegate.encode(response) : response.toMap();
141+
}
78142

79143
}

spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,9 @@
1818

1919
import java.util.List;
2020

21-
import org.apache.commons.logging.Log;
22-
import org.apache.commons.logging.LogFactory;
2321
import reactor.core.publisher.Mono;
2422

2523
import org.springframework.graphql.server.WebGraphQlHandler;
26-
import org.springframework.graphql.server.WebGraphQlRequest;
2724
import org.springframework.graphql.server.WebGraphQlResponse;
2825
import org.springframework.http.MediaType;
2926
import org.springframework.http.codec.CodecConfigurer;
@@ -39,8 +36,6 @@
3936
*/
4037
public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler {
4138

42-
private static final Log logger = LogFactory.getLog(GraphQlHttpHandler.class);
43-
4439
@SuppressWarnings("removal")
4540
private static final List<MediaType> SUPPORTED_MEDIA_TYPES = List.of(
4641
MediaType.APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_JSON, MediaType.APPLICATION_GRAPHQL);
@@ -60,43 +55,15 @@ public GraphQlHttpHandler(WebGraphQlHandler graphQlHandler) {
6055
* @param codecConfigurer codec configurer for JSON encoding and decoding
6156
*/
6257
public GraphQlHttpHandler(WebGraphQlHandler graphQlHandler, CodecConfigurer codecConfigurer) {
63-
super(graphQlHandler, new HttpCodecDelegate(codecConfigurer));
58+
super(graphQlHandler, codecConfigurer);
6459
}
6560

6661

67-
/**
68-
* Handle GraphQL requests over HTTP.
69-
* @param request the incoming HTTP request
70-
* @return the HTTP response
71-
*/
72-
public Mono<ServerResponse> handleRequest(ServerRequest request) {
73-
return readRequest(request)
74-
.flatMap((body) -> {
75-
WebGraphQlRequest graphQlRequest = new WebGraphQlRequest(
76-
request.uri(), request.headers().asHttpHeaders(),
77-
request.cookies(), request.remoteAddress().orElse(null),
78-
request.attributes(), body,
79-
request.exchange().getRequest().getId(),
80-
request.exchange().getLocaleContext().getLocale());
81-
if (logger.isDebugEnabled()) {
82-
logger.debug("Executing: " + graphQlRequest);
83-
}
84-
return this.graphQlHandler.handleRequest(graphQlRequest);
85-
})
86-
.flatMap((response) -> {
87-
if (logger.isDebugEnabled()) {
88-
logger.debug("Execution result ready");
89-
}
90-
return prepareResponse(request, response);
91-
});
92-
}
93-
94-
protected Mono<ServerResponse> prepareResponse(ServerRequest serverRequest, WebGraphQlResponse response) {
62+
protected Mono<ServerResponse> prepareResponse(ServerRequest request, WebGraphQlResponse response) {
9563
ServerResponse.BodyBuilder builder = ServerResponse.ok();
9664
builder.headers((headers) -> headers.putAll(response.getResponseHeaders()));
97-
builder.contentType(selectResponseMediaType(serverRequest));
98-
return builder.bodyValue((this.codecDelegate != null) ?
99-
this.codecDelegate.encode(response) : response.toMap());
65+
builder.contentType(selectResponseMediaType(request));
66+
return builder.bodyValue(encodeResponseIfNecessary(response));
10067
}
10168

10269
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {

spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java

Lines changed: 29 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,20 @@
1818

1919

2020
import java.util.Collections;
21-
import java.util.List;
2221
import java.util.Map;
2322

2423
import graphql.ErrorType;
2524
import graphql.ExecutionResult;
2625
import graphql.GraphQLError;
27-
import org.apache.commons.logging.Log;
28-
import org.apache.commons.logging.LogFactory;
2926
import org.reactivestreams.Publisher;
3027
import reactor.core.publisher.Flux;
3128
import reactor.core.publisher.Mono;
3229

33-
import org.springframework.graphql.ResponseError;
3430
import org.springframework.graphql.execution.SubscriptionPublisherException;
3531
import org.springframework.graphql.server.WebGraphQlHandler;
36-
import org.springframework.graphql.server.WebGraphQlRequest;
32+
import org.springframework.graphql.server.WebGraphQlResponse;
3733
import org.springframework.http.MediaType;
3834
import org.springframework.http.codec.ServerSentEvent;
39-
import org.springframework.util.CollectionUtils;
4035
import org.springframework.web.reactive.function.BodyInserters;
4136
import org.springframework.web.reactive.function.server.ServerRequest;
4237
import org.springframework.web.reactive.function.server.ServerResponse;
@@ -53,8 +48,6 @@
5348
*/
5449
public class GraphQlSseHandler extends AbstractGraphQlHttpHandler {
5550

56-
private static final Log logger = LogFactory.getLog(GraphQlSseHandler.class);
57-
5851
private static final Mono<ServerSentEvent<Map<String, Object>>> COMPLETE_EVENT = Mono.just(
5952
ServerSentEvent.<Map<String, Object>>builder(Collections.emptyMap()).event("complete").build());
6053

@@ -63,59 +56,37 @@ public GraphQlSseHandler(WebGraphQlHandler graphQlHandler) {
6356
super(graphQlHandler, null);
6457
}
6558

66-
/**
67-
* Handle GraphQL requests over HTTP using the Server-Sent Events protocol.
68-
* @param serverRequest the incoming HTTP request
69-
* @return the HTTP response
70-
*/
59+
7160
@SuppressWarnings("unchecked")
72-
public Mono<ServerResponse> handleRequest(ServerRequest serverRequest) {
73-
return readRequest(serverRequest)
74-
.flatMap((body) -> {
75-
WebGraphQlRequest graphQlRequest = new WebGraphQlRequest(
76-
serverRequest.uri(), serverRequest.headers().asHttpHeaders(),
77-
serverRequest.cookies(), serverRequest.remoteAddress().orElse(null),
78-
serverRequest.attributes(), body,
79-
serverRequest.exchange().getRequest().getId(),
80-
serverRequest.exchange().getLocaleContext().getLocale());
81-
if (logger.isDebugEnabled()) {
82-
logger.debug("Executing: " + graphQlRequest);
83-
}
84-
return this.graphQlHandler.handleRequest(graphQlRequest);
85-
})
86-
.flatMap((response) -> {
87-
if (logger.isDebugEnabled()) {
88-
List<ResponseError> errors = response.getErrors();
89-
logger.debug("Execution result " +
90-
(!CollectionUtils.isEmpty(errors) ? "has errors: " + errors : "is ready") + ".");
91-
}
92-
Flux<Map<String, Object>> resultFlux;
93-
if (response.getData() instanceof Publisher) {
94-
resultFlux = Flux.from((Publisher<ExecutionResult>) response.getData())
95-
.map(ExecutionResult::toSpecification)
96-
.onErrorResume(SubscriptionPublisherException.class, (ex) -> Mono.just(ex.toMap()));
97-
}
98-
else {
99-
if (logger.isDebugEnabled()) {
100-
logger.debug("A subscription DataFetcher must return a Publisher: " + response.getData());
101-
}
102-
resultFlux = Flux.just(ExecutionResult.newExecutionResult()
103-
.addError(GraphQLError.newError()
104-
.errorType(ErrorType.OperationNotSupported)
105-
.message("SSE handler supports only subscriptions")
106-
.build())
107-
.build()
108-
.toSpecification());
109-
}
61+
@Override
62+
protected Mono<ServerResponse> prepareResponse(ServerRequest request, WebGraphQlResponse response) {
63+
64+
Flux<Map<String, Object>> resultFlux;
65+
if (response.getData() instanceof Publisher) {
66+
resultFlux = Flux.from((Publisher<ExecutionResult>) response.getData())
67+
.map(ExecutionResult::toSpecification)
68+
.onErrorResume(SubscriptionPublisherException.class, (ex) -> Mono.just(ex.toMap()));
69+
}
70+
else {
71+
if (this.logger.isDebugEnabled()) {
72+
this.logger.debug("A subscription DataFetcher must return a Publisher: " + response.getData());
73+
}
74+
resultFlux = Flux.just(ExecutionResult.newExecutionResult()
75+
.addError(GraphQLError.newError()
76+
.errorType(ErrorType.OperationNotSupported)
77+
.message("SSE handler supports only subscriptions")
78+
.build())
79+
.build()
80+
.toSpecification());
81+
}
11082

111-
Flux<ServerSentEvent<Map<String, Object>>> sseFlux =
112-
resultFlux.map((event) -> ServerSentEvent.builder(event).event("next").build());
83+
Flux<ServerSentEvent<Map<String, Object>>> sseFlux =
84+
resultFlux.map((event) -> ServerSentEvent.builder(event).event("next").build());
11385

114-
return ServerResponse.ok()
115-
.contentType(MediaType.TEXT_EVENT_STREAM)
116-
.body(BodyInserters.fromServerSentEvents(sseFlux.concatWith(COMPLETE_EVENT)))
117-
.onErrorResume(Throwable.class, (ex) -> ServerResponse.badRequest().build());
118-
});
86+
return ServerResponse.ok()
87+
.contentType(MediaType.TEXT_EVENT_STREAM)
88+
.body(BodyInserters.fromServerSentEvents(sseFlux.concatWith(COMPLETE_EVENT)))
89+
.onErrorResume(Throwable.class, (ex) -> ServerResponse.badRequest().build());
11990
}
12091

12192
}

0 commit comments

Comments
 (0)