Polishing in GraphQlHttpHandler
This commit is contained in:
@@ -76,14 +76,15 @@ public abstract class AbstractGraphQlHttpHandler {
|
|||||||
return readRequest(request)
|
return readRequest(request)
|
||||||
.flatMap((body) -> {
|
.flatMap((body) -> {
|
||||||
WebGraphQlRequest graphQlRequest = new WebGraphQlRequest(
|
WebGraphQlRequest graphQlRequest = new WebGraphQlRequest(
|
||||||
request.uri(), request.headers().asHttpHeaders(),
|
request.uri(), request.headers().asHttpHeaders(), request.cookies(),
|
||||||
request.cookies(), request.remoteAddress().orElse(null),
|
request.remoteAddress().orElse(null), request.attributes(), body,
|
||||||
request.attributes(), body,
|
|
||||||
request.exchange().getRequest().getId(),
|
request.exchange().getRequest().getId(),
|
||||||
request.exchange().getLocaleContext().getLocale());
|
request.exchange().getLocaleContext().getLocale());
|
||||||
|
|
||||||
if (this.logger.isDebugEnabled()) {
|
if (this.logger.isDebugEnabled()) {
|
||||||
this.logger.debug("Executing: " + graphQlRequest);
|
this.logger.debug("Executing: " + graphQlRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.graphQlHandler.handleRequest(graphQlRequest);
|
return this.graphQlHandler.handleRequest(graphQlRequest);
|
||||||
})
|
})
|
||||||
.flatMap((response) -> {
|
.flatMap((response) -> {
|
||||||
@@ -92,6 +93,7 @@ public abstract class AbstractGraphQlHttpHandler {
|
|||||||
this.logger.debug("Execution result " +
|
this.logger.debug("Execution result " +
|
||||||
(!CollectionUtils.isEmpty(errors) ? "has errors: " + errors : "is ready") + ".");
|
(!CollectionUtils.isEmpty(errors) ? "has errors: " + errors : "is ready") + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
return prepareResponse(request, response);
|
return prepareResponse(request, response);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,12 @@ package org.springframework.graphql.server.webmvc;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.Cookie;
|
import jakarta.servlet.http.Cookie;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
@@ -36,7 +39,9 @@ import org.springframework.http.HttpCookie;
|
|||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.http.server.ServerHttpRequest;
|
||||||
import org.springframework.http.server.ServletServerHttpRequest;
|
import org.springframework.http.server.ServletServerHttpRequest;
|
||||||
|
import org.springframework.http.server.ServletServerHttpResponse;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.AlternativeJdkIdGenerator;
|
import org.springframework.util.AlternativeJdkIdGenerator;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@@ -46,6 +51,7 @@ import org.springframework.util.LinkedMultiValueMap;
|
|||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||||
import org.springframework.web.server.ServerWebInputException;
|
import org.springframework.web.server.ServerWebInputException;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.function.ServerRequest;
|
import org.springframework.web.servlet.function.ServerRequest;
|
||||||
import org.springframework.web.servlet.function.ServerResponse;
|
import org.springframework.web.servlet.function.ServerResponse;
|
||||||
|
|
||||||
@@ -79,11 +85,19 @@ public abstract class AbstractGraphQlHttpHandler {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the custom message converter, if configured, to read and write with.
|
* Exposes a {@link ServerResponse.HeadersBuilder.WriteFunction} that writes
|
||||||
|
* with the {@code HttpMessageConverter} provided to the constructor.
|
||||||
|
* @param resultMap the result map to write
|
||||||
|
* @param contentType to set the response content type to
|
||||||
|
* @return the write function, or {@code null} if a
|
||||||
|
* {@code HttpMessageConverter} was not provided to the constructor
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public HttpMessageConverter<Object> getMessageConverter() {
|
protected ServerResponse.HeadersBuilder.WriteFunction getWriteFunction(
|
||||||
return this.messageConverter;
|
Map<String, Object> resultMap, MediaType contentType) {
|
||||||
|
|
||||||
|
return (this.messageConverter != null) ?
|
||||||
|
new MessageConverterWriteFunction(resultMap, contentType, this.messageConverter) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -133,8 +147,8 @@ public abstract class AbstractGraphQlHttpHandler {
|
|||||||
if (this.messageConverter != null) {
|
if (this.messageConverter != null) {
|
||||||
MediaType contentType = request.headers().contentType().orElse(MediaType.APPLICATION_JSON);
|
MediaType contentType = request.headers().contentType().orElse(MediaType.APPLICATION_JSON);
|
||||||
if (this.messageConverter.canRead(SerializableGraphQlRequest.class, contentType)) {
|
if (this.messageConverter.canRead(SerializableGraphQlRequest.class, contentType)) {
|
||||||
return (GraphQlRequest) this.messageConverter.read(SerializableGraphQlRequest.class,
|
ServerHttpRequest httpRequest = new ServletServerHttpRequest(request.servletRequest());
|
||||||
new ServletServerHttpRequest(request.servletRequest()));
|
return (GraphQlRequest) this.messageConverter.read(SerializableGraphQlRequest.class, httpRequest);
|
||||||
}
|
}
|
||||||
throw new HttpMediaTypeNotSupportedException(
|
throw new HttpMediaTypeNotSupportedException(
|
||||||
contentType, this.messageConverter.getSupportedMediaTypes(), request.method());
|
contentType, this.messageConverter.getSupportedMediaTypes(), request.method());
|
||||||
@@ -181,4 +195,20 @@ public abstract class AbstractGraphQlHttpHandler {
|
|||||||
protected abstract ServerResponse prepareResponse(
|
protected abstract ServerResponse prepareResponse(
|
||||||
ServerRequest request, Mono<WebGraphQlResponse> responseMono) throws ServletException;
|
ServerRequest request, Mono<WebGraphQlResponse> responseMono) throws ServletException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WriteFunction that writes with a given, fixed {@link HttpMessageConverter}.
|
||||||
|
*/
|
||||||
|
private record MessageConverterWriteFunction(
|
||||||
|
Map<String, Object> resultMap, MediaType contentType, HttpMessageConverter<Object> converter)
|
||||||
|
implements ServerResponse.HeadersBuilder.WriteFunction {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelAndView write(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
|
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
||||||
|
this.converter.write(this.resultMap, this.contentType, httpResponse);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import org.springframework.graphql.server.WebGraphQlHandler;
|
|||||||
import org.springframework.graphql.server.WebGraphQlResponse;
|
import org.springframework.graphql.server.WebGraphQlResponse;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.server.ServletServerHttpResponse;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.web.servlet.function.ServerRequest;
|
import org.springframework.web.servlet.function.ServerRequest;
|
||||||
import org.springframework.web.servlet.function.ServerResponse;
|
import org.springframework.web.servlet.function.ServerResponse;
|
||||||
@@ -56,12 +55,12 @@ public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance with a custom message converter.
|
* Create a new instance with a custom message converter for GraphQL payloads.
|
||||||
* <p>If no converter is provided, this will use
|
* <p>If no converter is provided, the handler will use
|
||||||
* {@link org.springframework.web.servlet.config.annotation.WebMvcConfigurer#configureMessageConverters(List)
|
* {@link org.springframework.web.servlet.config.annotation.WebMvcConfigurer#configureMessageConverters(List)
|
||||||
* the one configured in the web framework}.
|
* the one configured for web use}.
|
||||||
* @param graphQlHandler common handler for GraphQL over HTTP requests
|
* @param graphQlHandler common handler for GraphQL over HTTP requests
|
||||||
* @param converter custom {@link HttpMessageConverter} to read and write GraphQL payloads
|
* @param converter the converter to use to read and write GraphQL payloads
|
||||||
*/
|
*/
|
||||||
public GraphQlHttpHandler(WebGraphQlHandler graphQlHandler, @Nullable HttpMessageConverter<?> converter) {
|
public GraphQlHttpHandler(WebGraphQlHandler graphQlHandler, @Nullable HttpMessageConverter<?> converter) {
|
||||||
super(graphQlHandler, converter);
|
super(graphQlHandler, converter);
|
||||||
@@ -77,15 +76,12 @@ public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler {
|
|||||||
builder.headers((headers) -> headers.putAll(response.getResponseHeaders()));
|
builder.headers((headers) -> headers.putAll(response.getResponseHeaders()));
|
||||||
builder.contentType(contentType);
|
builder.contentType(contentType);
|
||||||
|
|
||||||
if (getMessageConverter() != null) {
|
Map<String, Object> resultMap = response.toMap();
|
||||||
return builder.build(writeFunction(getMessageConverter(), contentType, response.toMap()));
|
ServerResponse.HeadersBuilder.WriteFunction writer = getWriteFunction(resultMap, contentType);
|
||||||
}
|
return (writer != null) ? builder.build(writer) : builder.body(resultMap);
|
||||||
else {
|
|
||||||
return builder.body(response.toMap());
|
|
||||||
}
|
|
||||||
}).toFuture();
|
}).toFuture();
|
||||||
|
|
||||||
// This won't be needed with a Spring Framework 6.2 baseline:
|
// This won't be needed on a Spring Framework 6.2 baseline:
|
||||||
// https://github.com/spring-projects/spring-framework/issues/32223
|
// https://github.com/spring-projects/spring-framework/issues/32223
|
||||||
|
|
||||||
if (future.isDone() && !future.isCancelled() && !future.isCompletedExceptionally()) {
|
if (future.isDone() && !future.isCancelled() && !future.isCompletedExceptionally()) {
|
||||||
@@ -100,23 +96,13 @@ public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler {
|
|||||||
return ServerResponse.async(future);
|
return ServerResponse.async(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {
|
private static MediaType selectResponseMediaType(ServerRequest request) {
|
||||||
for (MediaType accepted : serverRequest.headers().accept()) {
|
for (MediaType mediaType : request.headers().accept()) {
|
||||||
if (SUPPORTED_MEDIA_TYPES.contains(accepted)) {
|
if (SUPPORTED_MEDIA_TYPES.contains(mediaType)) {
|
||||||
return accepted;
|
return mediaType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MediaType.APPLICATION_JSON;
|
return MediaType.APPLICATION_JSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ServerResponse.HeadersBuilder.WriteFunction writeFunction(
|
|
||||||
HttpMessageConverter<Object> converter, MediaType contentType, Map<String, Object> resultMap) {
|
|
||||||
|
|
||||||
return (servletRequest, servletResponse) -> {
|
|
||||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(servletResponse);
|
|
||||||
converter.write(resultMap, contentType, httpResponse);
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user