Skip to content

Commit fb1f0ee

Browse files
committed
Deserialize HTTP request body to POJO
Closes gh-905
1 parent 1736d2a commit fb1f0ee

15 files changed

+220
-137
lines changed

spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -79,7 +79,7 @@ public CodecConfigurer getCodecConfigurer() {
7979
protected Mono<ExecutionGraphQlResponse> executeInternal(ExecutionGraphQlRequest executionRequest) {
8080

8181
WebGraphQlRequest request = new WebGraphQlRequest(
82-
this.url, this.headers, null, Collections.emptyMap(), executionRequest.toMap(),
82+
this.url, this.headers, null, Collections.emptyMap(), executionRequest,
8383
idGenerator.generateId().toString(), null);
8484

8585
return this.graphQlHandler.handleRequest(request).cast(ExecutionGraphQlResponse.class);

spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323

2424
import org.springframework.graphql.ExecutionGraphQlRequest;
25+
import org.springframework.graphql.GraphQlRequest;
2526
import org.springframework.graphql.support.DefaultExecutionGraphQlRequest;
2627
import org.springframework.http.HttpCookie;
2728
import org.springframework.http.HttpHeaders;
@@ -60,15 +61,6 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements
6061
private final Map<String, Object> attributes;
6162

6263

63-
/**
64-
* Create an instance.
65-
* @deprecated as of 1.1.3 in favor of the constructor with cookies
66-
*/
67-
@Deprecated
68-
public WebGraphQlRequest(URI uri, HttpHeaders headers, Map<String, Object> body, String id, @Nullable Locale locale) {
69-
this(uri, headers, null, Collections.emptyMap(), body, id, locale);
70-
}
71-
7264
/**
7365
* Create an instance.
7466
* @param uri the URL for the HTTP request or WebSocket handshake
@@ -78,22 +70,27 @@ public WebGraphQlRequest(URI uri, HttpHeaders headers, Map<String, Object> body,
7870
* @param body the deserialized content of the GraphQL request
7971
* @param id an identifier for the GraphQL request
8072
* @param locale the locale from the HTTP request, if any
73+
* @since 1.2.5
74+
*/
75+
public WebGraphQlRequest(
76+
URI uri, HttpHeaders headers, @Nullable MultiValueMap<String, HttpCookie> cookies,
77+
Map<String, Object> attributes, GraphQlRequest body, String id, @Nullable Locale locale) {
78+
79+
this(uri, headers, cookies, attributes, body.getDocument(),
80+
body.getOperationName(), body.getVariables(), body.getExtensions(), id, locale);
81+
}
82+
83+
/**
84+
* Variant of {@link #WebGraphQlRequest(URI, HttpHeaders, MultiValueMap, Map, GraphQlRequest, String, Locale)}
85+
* with a Map for the request body.
8186
* @since 1.1.3
8287
*/
8388
public WebGraphQlRequest(
8489
URI uri, HttpHeaders headers, @Nullable MultiValueMap<String, HttpCookie> cookies,
8590
Map<String, Object> attributes, Map<String, Object> body, String id, @Nullable Locale locale) {
8691

87-
super(getQuery(body), getOperation(body),
92+
this(uri, headers, cookies, attributes, getQuery(body), getOperation(body),
8893
getMap(VARIABLES_KEY, body), getMap(EXTENSIONS_KEY, body), id, locale);
89-
90-
Assert.notNull(uri, "URI is required'");
91-
Assert.notNull(headers, "HttpHeaders is required'");
92-
93-
this.uri = UriComponentsBuilder.fromUri(uri).build(true);
94-
this.headers = headers;
95-
this.cookies = (cookies != null ? CollectionUtils.unmodifiableMultiValueMap(cookies) : EMPTY_COOKIES);
96-
this.attributes = Collections.unmodifiableMap(attributes);
9794
}
9895

9996
private static String getQuery(Map<String, Object> body) {
@@ -123,6 +120,33 @@ private static Map<String, Object> getMap(String key, Map<String, Object> body)
123120
return (Map<String, Object>) value;
124121
}
125122

123+
/**
124+
* Create an instance.
125+
* @deprecated as of 1.1.3 in favor of
126+
* {@link #WebGraphQlRequest(URI, HttpHeaders, MultiValueMap, Map, GraphQlRequest, String, Locale)}
127+
*/
128+
@Deprecated(since = "1.1.3", forRemoval = true)
129+
public WebGraphQlRequest(URI uri, HttpHeaders headers, Map<String, Object> body, String id, @Nullable Locale locale) {
130+
this(uri, headers, null, Collections.emptyMap(), body, id, locale);
131+
}
132+
133+
private WebGraphQlRequest(
134+
URI uri, HttpHeaders headers, @Nullable MultiValueMap<String, HttpCookie> cookies,
135+
Map<String, Object> attributes, String document, @Nullable String operationName,
136+
@Nullable Map<String, Object> variables, @Nullable Map<String, Object> extensions,
137+
String id, @Nullable Locale locale) {
138+
139+
super(document, operationName, variables, extensions, id, locale);
140+
141+
Assert.notNull(uri, "URI is required'");
142+
Assert.notNull(headers, "HttpHeaders is required'");
143+
144+
this.uri = UriComponentsBuilder.fromUri(uri).build(true);
145+
this.headers = headers;
146+
this.cookies = (cookies != null ? CollectionUtils.unmodifiableMultiValueMap(cookies) : EMPTY_COOKIES);
147+
this.attributes = Collections.unmodifiableMap(attributes);
148+
}
149+
126150

127151
/**
128152
* Return the URL for the HTTP request or WebSocket handshake.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.graphql.server.support;
17+
18+
import java.util.Map;
19+
20+
import org.springframework.graphql.GraphQlRequest;
21+
import org.springframework.lang.Nullable;
22+
import org.springframework.web.server.ServerWebInputException;
23+
24+
25+
/**
26+
* {@link GraphQlRequest} for deserialization from a request.
27+
*
28+
* @author Rossen Stoyanchev
29+
* @since 1.2.5
30+
*/
31+
public class SerializableGraphQlRequest implements GraphQlRequest {
32+
33+
@Nullable
34+
private String query;
35+
36+
@Nullable
37+
private String operationName;
38+
39+
@Nullable
40+
private Map<String, Object> variables;
41+
42+
@Nullable
43+
private Map<String, Object> extensions;
44+
45+
46+
public void setQuery(String query) {
47+
this.query = query;
48+
}
49+
50+
@Nullable
51+
public String getQuery() {
52+
return this.query;
53+
}
54+
55+
public void setOperationName(@Nullable String operationName) {
56+
this.operationName = operationName;
57+
}
58+
59+
@Nullable
60+
@Override
61+
public String getOperationName() {
62+
return this.operationName;
63+
}
64+
65+
public void setVariables(Map<String, Object> variables) {
66+
this.variables = variables;
67+
}
68+
69+
@Nullable
70+
@Override
71+
public Map<String, Object> getVariables() {
72+
return this.variables;
73+
}
74+
75+
public void setExtensions(Map<String, Object> extensions) {
76+
this.extensions = extensions;
77+
}
78+
79+
@Nullable
80+
@Override
81+
public Map<String, Object> getExtensions() {
82+
return this.extensions;
83+
}
84+
85+
@Override
86+
public String getDocument() {
87+
if (this.query == null) {
88+
throw new ServerWebInputException("No 'query'");
89+
}
90+
return this.query;
91+
}
92+
93+
@Override
94+
public Map<String, Object> toMap() {
95+
throw new UnsupportedOperationException();
96+
}
97+
98+
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 the original author or authors.
2+
* Copyright 2020-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import org.springframework.core.ParameterizedTypeReference;
2828
import org.springframework.graphql.server.WebGraphQlHandler;
2929
import org.springframework.graphql.server.WebGraphQlRequest;
30+
import org.springframework.graphql.server.support.SerializableGraphQlRequest;
3031
import org.springframework.http.MediaType;
3132
import org.springframework.util.Assert;
3233
import org.springframework.web.reactive.function.server.ServerRequest;
@@ -71,7 +72,7 @@ public GraphQlHttpHandler(WebGraphQlHandler graphQlHandler) {
7172
* @return the HTTP response
7273
*/
7374
public Mono<ServerResponse> handleRequest(ServerRequest serverRequest) {
74-
return serverRequest.bodyToMono(MAP_PARAMETERIZED_TYPE_REF)
75+
return serverRequest.bodyToMono(SerializableGraphQlRequest.class)
7576
.flatMap(body -> {
7677
WebGraphQlRequest graphQlRequest = new WebGraphQlRequest(
7778
serverRequest.uri(), serverRequest.headers().asHttpHeaders(),

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030

3131
import org.springframework.context.i18n.LocaleContextHolder;
3232
import org.springframework.core.ParameterizedTypeReference;
33+
import org.springframework.graphql.GraphQlRequest;
3334
import org.springframework.graphql.server.WebGraphQlHandler;
3435
import org.springframework.graphql.server.WebGraphQlRequest;
36+
import org.springframework.graphql.server.support.SerializableGraphQlRequest;
3537
import org.springframework.http.HttpCookie;
3638
import org.springframework.http.MediaType;
3739
import org.springframework.util.AlternativeJdkIdGenerator;
@@ -135,9 +137,9 @@ private static MultiValueMap<String, HttpCookie> initCookies(ServerRequest serve
135137
return target;
136138
}
137139

138-
private static Map<String, Object> readBody(ServerRequest request) throws ServletException {
140+
private static GraphQlRequest readBody(ServerRequest request) throws ServletException {
139141
try {
140-
return request.body(MAP_PARAMETERIZED_TYPE_REF);
142+
return request.body(SerializableGraphQlRequest.class);
141143
}
142144
catch (IOException ex) {
143145
throw new ServerWebInputException("I/O error while reading request body", null, ex);

spring-graphql/src/test/java/org/springframework/graphql/data/query/QuerydslDataFetcherTests.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,14 +49,13 @@
4949
import org.springframework.graphql.GraphQlSetup;
5050
import org.springframework.graphql.ResponseHelper;
5151
import org.springframework.graphql.data.GraphQlRepository;
52-
import org.springframework.graphql.data.pagination.ConnectionFieldTypeVisitor;
5352
import org.springframework.graphql.data.query.QuerydslDataFetcher.Builder;
5453
import org.springframework.graphql.data.query.QuerydslDataFetcher.QuerydslBuilderCustomizer;
55-
import org.springframework.graphql.execution.ConnectionTypeDefinitionConfigurer;
5654
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
5755
import org.springframework.graphql.server.WebGraphQlHandler;
5856
import org.springframework.graphql.server.WebGraphQlRequest;
5957
import org.springframework.graphql.server.WebGraphQlResponse;
58+
import org.springframework.graphql.support.DefaultGraphQlRequest;
6059
import org.springframework.http.HttpHeaders;
6160
import org.springframework.lang.Nullable;
6261

@@ -378,7 +377,7 @@ private static RuntimeWiringConfigurer createRuntimeWiringConfigurer(
378377
private WebGraphQlRequest request(String query) {
379378
return new WebGraphQlRequest(
380379
URI.create("/"), new HttpHeaders(), null, Collections.emptyMap(),
381-
Collections.singletonMap("query", query), "1", Locale.ENGLISH);
380+
new DefaultGraphQlRequest(query), "1", Locale.ENGLISH);
382381
}
383382

384383

spring-graphql/src/test/java/org/springframework/graphql/data/query/jpa/QueryByExampleDataFetcherJpaTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,6 +51,7 @@
5151
import org.springframework.graphql.server.WebGraphQlHandler;
5252
import org.springframework.graphql.server.WebGraphQlRequest;
5353
import org.springframework.graphql.server.WebGraphQlResponse;
54+
import org.springframework.graphql.support.DefaultGraphQlRequest;
5455
import org.springframework.http.HttpHeaders;
5556
import org.springframework.jdbc.datasource.DriverManagerDataSource;
5657
import org.springframework.orm.jpa.JpaTransactionManager;
@@ -262,7 +263,7 @@ private static RuntimeWiringConfigurer createRuntimeWiringConfigurer(QueryByExam
262263
private WebGraphQlRequest request(String query) {
263264
return new WebGraphQlRequest(
264265
URI.create("/"), new HttpHeaders(), null, Collections.emptyMap(),
265-
Collections.singletonMap("query", query), "1", null);
266+
new DefaultGraphQlRequest(query), "1", null);
266267
}
267268

268269

spring-graphql/src/test/java/org/springframework/graphql/data/query/mongo/QueryByExampleDataFetcherMongoDbTests.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,16 +45,15 @@
4545
import org.springframework.graphql.BookSource;
4646
import org.springframework.graphql.GraphQlSetup;
4747
import org.springframework.graphql.ResponseHelper;
48-
import org.springframework.graphql.data.pagination.ConnectionFieldTypeVisitor;
4948
import org.springframework.graphql.data.query.QueryByExampleDataFetcher;
5049
import org.springframework.graphql.data.query.ScrollPositionCursorStrategy;
5150
import org.springframework.graphql.data.query.ScrollSubrange;
5251
import org.springframework.graphql.data.query.WindowConnectionAdapter;
53-
import org.springframework.graphql.execution.ConnectionTypeDefinitionConfigurer;
5452
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
5553
import org.springframework.graphql.server.WebGraphQlHandler;
5654
import org.springframework.graphql.server.WebGraphQlRequest;
5755
import org.springframework.graphql.server.WebGraphQlResponse;
56+
import org.springframework.graphql.support.DefaultGraphQlRequest;
5857
import org.springframework.http.HttpHeaders;
5958
import org.springframework.lang.Nullable;
6059
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@@ -242,7 +241,7 @@ private static RuntimeWiringConfigurer createRuntimeWiringConfigurer(QueryByExam
242241
private WebGraphQlRequest request(String query) {
243242
return new WebGraphQlRequest(
244243
URI.create("/"), new HttpHeaders(), null, Collections.emptyMap(),
245-
Collections.singletonMap("query", query), "1", null);
244+
new DefaultGraphQlRequest(query), "1", null);
246245
}
247246

248247

spring-graphql/src/test/java/org/springframework/graphql/data/query/mongo/QueryByExampleDataFetcherReactiveMongoDbTests.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@
5252
import org.springframework.graphql.server.WebGraphQlHandler;
5353
import org.springframework.graphql.server.WebGraphQlRequest;
5454
import org.springframework.graphql.server.WebGraphQlResponse;
55+
import org.springframework.graphql.support.DefaultGraphQlRequest;
5556
import org.springframework.http.HttpHeaders;
5657
import org.springframework.lang.Nullable;
5758
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@@ -213,7 +214,7 @@ private static RuntimeWiringConfigurer createRuntimeWiringConfigurer(ReactiveQue
213214
private WebGraphQlRequest request(String query) {
214215
return new WebGraphQlRequest(
215216
URI.create("/"), new HttpHeaders(), null, Collections.emptyMap(),
216-
Collections.singletonMap("query", query), "1", null);
217+
new DefaultGraphQlRequest(query), "1", null);
217218
}
218219

219220

0 commit comments

Comments
 (0)