Polishing CORS support
This commit is contained in:
@@ -24,6 +24,7 @@ import java.util.List;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
@@ -37,29 +38,31 @@ import org.springframework.web.cors.CorsConfiguration;
|
||||
@ConfigurationProperties(prefix = "spring.graphql.cors")
|
||||
public class GraphQlCorsProperties {
|
||||
/**
|
||||
* Comma-separated list of origins to allow. '*' allows all origins. When credentials
|
||||
* are allowed, '*' cannot be used and origin patterns should be configured instead.
|
||||
* When no allowed origins or allowed origin patterns are set, CORS support is
|
||||
* disabled.
|
||||
* Comma-separated list of origins to allow with '*' allowing all origins.
|
||||
* When allow-credentials is enabled, '*' cannot be used, and setting
|
||||
* origin patterns should be considered instead.
|
||||
* When neither allowed origins nor allowed origin patterns are set,
|
||||
* cross-origin requests are effectively disabled.
|
||||
*/
|
||||
private List<String> allowedOrigins = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Comma-separated list of origin patterns to allow. Unlike allowed origins which only
|
||||
* supports '*', origin patterns are more flexible (for example
|
||||
* 'https://*.example.com') and can be used when credentials are allowed. When no
|
||||
* allowed origin patterns or allowed origins are set, CORS support is disabled.
|
||||
* Comma-separated list of origin patterns to allow. Unlike allowed origins
|
||||
* which only support '*', origin patterns are more flexible, e.g.
|
||||
* 'https://*.example.com', and can be used with allow-credentials.
|
||||
* When neither allowed origins nor allowed origin patterns are set,
|
||||
* cross-origin requests are effectively disabled.
|
||||
*/
|
||||
private List<String> allowedOriginPatterns = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Comma-separated list of methods to allow. '*' allows all methods. When not set,
|
||||
* defaults to GET.
|
||||
* Comma-separated list of HTTP methods to allow. '*' allows all methods.
|
||||
* When not set, defaults to GET.
|
||||
*/
|
||||
private List<String> allowedMethods = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Comma-separated list of headers to allow in a request. '*' allows all headers.
|
||||
* Comma-separated list of HTTP headers to allow in a request. '*' allows all headers.
|
||||
*/
|
||||
private List<String> allowedHeaders = new ArrayList<>();
|
||||
|
||||
@@ -71,6 +74,7 @@ public class GraphQlCorsProperties {
|
||||
/**
|
||||
* Whether credentials are supported. When not set, credentials are not supported.
|
||||
*/
|
||||
@Nullable
|
||||
private Boolean allowCredentials;
|
||||
|
||||
/**
|
||||
@@ -120,6 +124,7 @@ public class GraphQlCorsProperties {
|
||||
this.exposedHeaders = exposedHeaders;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getAllowCredentials() {
|
||||
return this.allowCredentials;
|
||||
}
|
||||
@@ -136,19 +141,20 @@ public class GraphQlCorsProperties {
|
||||
this.maxAge = maxAge;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CorsConfiguration toCorsConfiguration() {
|
||||
if (CollectionUtils.isEmpty(this.allowedOrigins) && CollectionUtils.isEmpty(this.allowedOriginPatterns)) {
|
||||
return null;
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
map.from(this::getAllowedOrigins).to(configuration::setAllowedOrigins);
|
||||
map.from(this::getAllowedOriginPatterns).to(configuration::setAllowedOriginPatterns);
|
||||
map.from(this::getAllowedHeaders).whenNot(CollectionUtils::isEmpty).to(configuration::setAllowedHeaders);
|
||||
map.from(this::getAllowedMethods).whenNot(CollectionUtils::isEmpty).to(configuration::setAllowedMethods);
|
||||
map.from(this::getExposedHeaders).whenNot(CollectionUtils::isEmpty).to(configuration::setExposedHeaders);
|
||||
map.from(this::getMaxAge).whenNonNull().as(Duration::getSeconds).to(configuration::setMaxAge);
|
||||
map.from(this::getAllowCredentials).whenNonNull().to(configuration::setAllowCredentials);
|
||||
return configuration;
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
map.from(this::getAllowedOrigins).to(config::setAllowedOrigins);
|
||||
map.from(this::getAllowedOriginPatterns).to(config::setAllowedOriginPatterns);
|
||||
map.from(this::getAllowedHeaders).whenNot(CollectionUtils::isEmpty).to(config::setAllowedHeaders);
|
||||
map.from(this::getAllowedMethods).whenNot(CollectionUtils::isEmpty).to(config::setAllowedMethods);
|
||||
map.from(this::getExposedHeaders).whenNot(CollectionUtils::isEmpty).to(config::setExposedHeaders);
|
||||
map.from(this::getMaxAge).whenNonNull().as(Duration::getSeconds).to(config::setMaxAge);
|
||||
map.from(this::getAllowCredentials).whenNonNull().to(config::setAllowCredentials);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,20 +155,20 @@ public class GraphQlWebFluxAutoConfiguration {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class GraphQlEndpointCorsConfiguration implements WebFluxConfigurer {
|
||||
|
||||
final GraphQlProperties graphql;
|
||||
final GraphQlProperties graphQlProperties;
|
||||
|
||||
final GraphQlCorsProperties cors;
|
||||
final GraphQlCorsProperties corsProperties;
|
||||
|
||||
public GraphQlEndpointCorsConfiguration(GraphQlProperties graphql, GraphQlCorsProperties cors) {
|
||||
this.graphql = graphql;
|
||||
this.cors = cors;
|
||||
public GraphQlEndpointCorsConfiguration(GraphQlProperties graphQlProps, GraphQlCorsProperties corsProps) {
|
||||
this.graphQlProperties = graphQlProps;
|
||||
this.corsProperties = corsProps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
CorsConfiguration configuration = cors.toCorsConfiguration();
|
||||
CorsConfiguration configuration = this.corsProperties.toCorsConfiguration();
|
||||
if (configuration != null) {
|
||||
registry.addMapping(graphql.getPath()).combine(configuration);
|
||||
registry.addMapping(this.graphQlProperties.getPath()).combine(configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,20 +176,20 @@ public class GraphQlWebMvcAutoConfiguration {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class GraphQlEndpointCorsConfiguration implements WebMvcConfigurer {
|
||||
|
||||
final GraphQlProperties graphql;
|
||||
final GraphQlProperties graphQlProperties;
|
||||
|
||||
final GraphQlCorsProperties cors;
|
||||
final GraphQlCorsProperties corsProperties;
|
||||
|
||||
public GraphQlEndpointCorsConfiguration(GraphQlProperties graphql, GraphQlCorsProperties cors) {
|
||||
this.graphql = graphql;
|
||||
this.cors = cors;
|
||||
public GraphQlEndpointCorsConfiguration(GraphQlProperties graphQlProps, GraphQlCorsProperties corsProps) {
|
||||
this.graphQlProperties = graphQlProps;
|
||||
this.corsProperties = corsProps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
CorsConfiguration configuration = cors.toCorsConfiguration();
|
||||
CorsConfiguration configuration = this.corsProperties.toCorsConfiguration();
|
||||
if (configuration != null) {
|
||||
registry.addMapping(graphql.getPath()).combine(configuration);
|
||||
registry.addMapping(this.graphQlProperties.getPath()).combine(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,9 +172,33 @@ Declare a `ThreadLocalAccessor` bean to assist with the propagation of `ThreadLo
|
||||
values of interest in <<index.adoc#execution-context-webmvc,Spring MVC>>.
|
||||
|
||||
|
||||
[[boot-graphql-cors]]
|
||||
== CORS
|
||||
|
||||
{spring-framework-ref-docs}/web.html#mvc-cors[Spring MVC] and
|
||||
{spring-framework-ref-docs}/web-reactive.html#webflux-cors[Spring WebFlux] support CORS
|
||||
(Cross-Origin Resource Sharing) requests. CORS is a critical part of the web config for
|
||||
GraphQL applications that are accessed from browsers using different domains.
|
||||
|
||||
The Boot starter supports the following CORS properties:
|
||||
|
||||
[source,properties,indent=0,subs="verbatim"]
|
||||
----
|
||||
spring.graphql.cors.allowed-origins=https://example.org # Comma-separated list of origins to allow. '*' allows all origins.
|
||||
spring.graphql.cors.allowed-origin-patterns= # Comma-separated list of origin patterns like 'https://*.example.com' to allow.
|
||||
spring.graphql.cors.allowed-methods=GET,POST # Comma-separated list of methods to allow. '*' allows all methods.
|
||||
spring.graphql.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers.
|
||||
spring.graphql.cors.exposed-headers= # Comma-separated list of headers to include in a response.
|
||||
spring.graphql.cors.allow-credentials= # Whether credentials are supported. When not set, credentials are not supported.
|
||||
spring.graphql.cors.max-age=1800s # How long the response from a pre-flight request can be cached by clients.
|
||||
----
|
||||
|
||||
TIP: For more information about the properties and their meaning, check out the
|
||||
{javadoc}/org/springframework/graphql/boot/GraphQlCorsProperties.html[GraphQlCorsProperties Javadoc].
|
||||
|
||||
|
||||
[[boot-graphql-exception-handling]]
|
||||
== Exception
|
||||
== Exceptions
|
||||
|
||||
Spring GraphQL enables applications to register one or more Spring
|
||||
`DataFetcherExceptionResolver` components that are invoked sequentially until one
|
||||
@@ -232,30 +256,6 @@ spring.graphql.graphiql.path=/graphiql
|
||||
----
|
||||
|
||||
|
||||
[[boot-graphql-cors]]
|
||||
== CORS configuration
|
||||
|
||||
Spring web frameworks all support CORS (Cross-Origin Resource Sharing), which is a critical part
|
||||
of your web configuration if your GraphQL API is meant to be accessed by browsers using different domains.
|
||||
|
||||
You can configure CORS support with properties:
|
||||
|
||||
[source,properties,indent=0,subs="verbatim"]
|
||||
----
|
||||
spring.graphql.cors.allowed-origins=https://example.org # Comma-separated list of origins to allow. '*' allows all origins.
|
||||
spring.graphql.cors.allowed-origin-patterns= # Comma-separated list of origin patterns like 'https://*.example.com' to allow.
|
||||
spring.graphql.cors.allowed-methods=GET,POST # Comma-separated list of methods to allow. '*' allows all methods.
|
||||
spring.graphql.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers.
|
||||
spring.graphql.cors.exposed-headers= # Comma-separated list of headers to include in a response.
|
||||
spring.graphql.cors.allow-credentials= # Whether credentials are supported. When not set, credentials are not supported.
|
||||
spring.graphql.cors.max-age=1800s # How long the response from a pre-flight request can be cached by clients.
|
||||
----
|
||||
|
||||
TIP: For more information about the properties and their meaning, check out the {javadoc}/org/springframework/graphql/boot/GraphQlCorsProperties.html[GraphQlCorsProperties Javadoc].
|
||||
|
||||
You can also learn more about CORS and Spring support in {spring-framework-ref-docs}/web.html#mvc-cors[Spring MVC] and
|
||||
{spring-framework-ref-docs}/web-reactive.html#webflux-cors[Spring WebFlux].
|
||||
|
||||
[[boot-graphql-metrics]]
|
||||
== Metrics
|
||||
|
||||
|
||||
Reference in New Issue
Block a user