Compare commits

...

8 Commits

Author SHA1 Message Date
Spring Builds 3a04435cd0 Release v6.0.2 2022-11-24 13:35:34 +00:00
Juergen Hoeller b7e99fbe30 Additional documentation notes on Java/Kotlin parameter name retention
See gh-29563
2022-11-24 14:08:53 +01:00
rstoyanchev a27f2e994b ResponseStatusException sets detail from reason again
Closes gh-29567
2022-11-24 12:40:25 +00:00
Juergen Hoeller 284cf3ecf2 Rely on standard parameter name resolution in Bean Validation 3.0
Just configuring additional Kotlin reflection if Kotlin is present.

Closes gh-29566
2022-11-24 12:01:51 +01:00
Juergen Hoeller cbf25b704d Reduce deprecation warn logging to one entry per introspected class
Closes gh-29563
2022-11-24 12:01:03 +01:00
Juergen Hoeller 8391897a94 Document removal of CommonsMultipartResolver in MVC setup documentation
Closes gh-29562
2022-11-24 12:00:19 +01:00
rstoyanchev a3c89092e1 Log connection info in StompBrokerRelayMessageHandler
See gh-29287
2022-11-23 13:58:37 +00:00
Spring Builds e77c426eeb Next development version (v6.0.2-SNAPSHOT) 2022-11-23 10:12:32 +00:00
17 changed files with 142 additions and 54 deletions
@@ -20,6 +20,9 @@ Feel free to join the #spring channel of https://slack.kotlinlang.org/[Kotlin Sl
or ask a question with `spring` and `kotlin` as tags on
https://stackoverflow.com/questions/tagged/spring+kotlin[Stackoverflow] if you need support.
[[kotlin-requirements]]
== Requirements
@@ -37,6 +40,9 @@ for serializing or deserializing JSON data for Kotlin classes with Jackson, so m
`com.fasterxml.jackson.module:jackson-module-kotlin` dependency to your project if you have such need.
It is automatically registered when found in the classpath.
[[kotlin-extensions]]
== Extensions
@@ -80,6 +86,9 @@ With Kotlin and the Spring Framework extensions, you can instead write the follo
As in Java, `users` in Kotlin is strongly typed, but Kotlin's clever type inference allows
for shorter syntax.
[[kotlin-null-safety]]
== Null-safety
@@ -115,6 +124,9 @@ NOTE: Generic type arguments, varargs, and array elements nullability are not su
but should be in an upcoming release. See https://github.com/Kotlin/KEEP/issues/79[this discussion]
for up-to-date information.
[[kotlin-classes-interfaces]]
== Classes and Interfaces
@@ -124,12 +136,16 @@ with default values.
Kotlin parameter names are recognized through a dedicated `KotlinReflectionParameterNameDiscoverer`,
which allows finding interface method parameter names without requiring the Java 8 `-parameters`
compiler flag to be enabled during compilation.
compiler flag to be enabled during compilation. (For completeness, we nevertheless recommend
running the Kotlin compiler with its `-java-parameters` flag for standard Java parameter exposure.)
You can declare configuration classes as
https://kotlinlang.org/docs/reference/nested-classes.html[top level or nested but not inner],
since the later requires a reference to the outer class.
[[kotlin-annotations]]
== Annotations
@@ -156,6 +172,9 @@ https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targe
such as `@field:NotNull` or `@get:Size(min=5, max=15)`, as described in
https://stackoverflow.com/a/35853200/1092077[this Stack Overflow response].
[[kotlin-bean-definition-dsl]]
== Bean Definition DSL
@@ -263,16 +282,20 @@ as the following example shows:
}
----
NOTE: Spring Boot is based on JavaConfig and
https://github.com/spring-projects/spring-boot/issues/8115[does not yet provide specific support for functional bean definition],
but you can experimentally use functional bean definitions through Spring Boot's `ApplicationContextInitializer` support.
See https://stackoverflow.com/questions/45935931/how-to-use-functional-bean-definition-kotlin-dsl-with-spring-boot-and-spring-w/46033685#46033685[this Stack Overflow answer]
for more details and up-to-date information. See also the experimental Kofu DSL developed in https://github.com/spring-projects/spring-fu[Spring Fu incubator].
[[kotlin-web]]
== Web
=== Router DSL
Spring Framework comes with a Kotlin router DSL available in 3 flavors:
@@ -314,6 +337,8 @@ when you need to register routes depending on dynamic data (for example, from a
See https://github.com/mixitconf/mixit/[MiXiT project] for a concrete example.
=== MockMvc DSL
A Kotlin DSL is provided via `MockMvc` Kotlin extensions in order to provide a more
@@ -339,6 +364,8 @@ mockMvc.get("/person/{name}", "Lee") {
}
----
=== Kotlin Script Templates
Spring Framework provides a
@@ -357,9 +384,7 @@ dependencies {
}
----
Configuration is usually done with `ScriptTemplateConfigurer` and `ScriptTemplateViewResolver`
beans.
Configuration is usually done with `ScriptTemplateConfigurer` and `ScriptTemplateViewResolver` beans.
`KotlinScriptConfiguration.kt`
[source,kotlin,indent=0]
@@ -386,6 +411,8 @@ class KotlinScriptConfiguration {
See the https://github.com/sdeleuze/kotlin-script-templating[kotlin-script-templating] example
project for more details.
=== Kotlin multiplatform serialization
As of Spring Framework 5.3, https://github.com/Kotlin/kotlinx.serialization[Kotlin multiplatform serialization] is
@@ -397,6 +424,9 @@ Kotlin serialization is designed to serialize only Kotlin classes annotated with
With Spring Messaging (RSocket), make sure that neither Jackson, GSON or JSONB are in the classpath if you want automatic configuration,
if Jackson is needed configure `KotlinSerializationJsonMessageConverter` manually.
== Coroutines
Kotlin https://kotlinlang.org/docs/reference/coroutines-overview.html[Coroutines] are Kotlin
@@ -415,6 +445,8 @@ Spring Framework provides support for Coroutines on the following scope:
* Suspending function and `Flow` support in RSocket `@MessageMapping` annotated methods
* Extensions for {docs-spring-framework}/kdoc-api/spring-messaging/org.springframework.messaging.rsocket/index.html[`RSocketRequester`]
=== Dependencies
Coroutines support is enabled when `kotlinx-coroutines-core` and `kotlinx-coroutines-reactor`
@@ -432,6 +464,8 @@ dependencies {
Version `1.4.0` and above are supported.
=== How Reactive translates to Coroutines?
For return values, the translation from Reactive to Coroutines APIs is the following:
@@ -458,6 +492,8 @@ https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coro
Read this blog post about https://spring.io/blog/2019/04/12/going-reactive-with-spring-coroutines-and-kotlin-flow[Going Reactive with Spring, Coroutines and Kotlin Flow]
for more details, including how to run code concurrently with Coroutines.
=== Controllers
Here is an example of a Coroutines `@RestController`.
@@ -554,6 +590,8 @@ class CoroutinesViewController(banner: Banner) {
}
----
=== WebFlux.fn
Here is an example of Coroutines router defined via the {docs-spring-framework}/kdoc-api/spring-webflux/org.springframework.web.reactive.function.server/co-router.html[coRouter { }] DSL and related handlers.
@@ -587,6 +625,8 @@ class UserHandler(builder: WebClient.Builder) {
}
----
=== Transactions
Transactions on Coroutines are supported via the programmatic variant of the Reactive
@@ -636,6 +676,8 @@ For Kotlin `Flow`, a `Flow<T>.transactional` extension is provided.
----
[[kotlin-spring-projects-in-kotlin]]
== Spring Projects in Kotlin
@@ -683,6 +725,8 @@ NOTE: The Kotlin code samples in Spring Framework documentation do not explicitl
`open` on the classes and their member functions. The samples are written for projects
using the `kotlin-allopen` plugin, since this is the most commonly used setup.
=== Using Immutable Class Instances for Persistence
In Kotlin, it is convenient and considered to be a best practice to declare read-only properties
@@ -726,6 +770,8 @@ NOTE: As of the Kay release train, Spring Data supports Kotlin immutable class i
does not require the `kotlin-noarg` plugin if the module uses Spring Data object mappings
(such as MongoDB, Redis, Cassandra, and others).
=== Injecting Dependencies
Our recommendation is to try to favor constructor injection with `val` read-only (and
@@ -761,6 +807,8 @@ as the following example shows:
}
----
=== Injecting Configuration Properties
In Java, you can inject configuration properties by using annotations (such as pass:q[`@Value("${property}")`)].
@@ -801,6 +849,7 @@ that uses the `${...}` syntax, with configuration beans, as the following exampl
----
=== Checked Exceptions
Java and https://kotlinlang.org/docs/reference/exceptions.html[Kotlin exception handling]
@@ -813,6 +862,8 @@ To get the original exception thrown like in Java, methods should be annotated w
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-throws/index.html[`@Throws`]
to specify explicitly the checked exceptions thrown (for example `@Throws(IOException::class)`).
=== Annotation Array Attributes
Kotlin annotations are mostly similar to Java annotations, but array attributes (which are
@@ -857,6 +908,8 @@ use a shortcut annotation, such as `@GetMapping`, `@PostMapping`, and others.
NOTE: If the `@RequestMapping` `method` attribute is not specified, all HTTP methods will
be matched, not only the `GET` method.
=== Testing
This section addresses testing with the combination of Kotlin and Spring Framework.
@@ -866,6 +919,7 @@ https://mockk.io/[Mockk] for mocking.
NOTE: If you are using Spring Boot, see
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-kotlin-testing[this related documentation].
==== Constructor injection
As described in the <<testing#testcontext-junit-jupiter-di, dedicated section>>,
@@ -887,6 +941,7 @@ class OrderServiceIntegrationTests(val orderService: OrderService,
----
====
==== `PER_CLASS` Lifecycle
Kotlin lets you specify meaningful test function names between backticks (```).
@@ -930,6 +985,7 @@ class IntegrationTests {
}
----
==== Specification-like Tests
You can create specification-like tests with JUnit 5 and Kotlin.
@@ -959,6 +1015,7 @@ class SpecificationLikeTests {
}
----
[[kotlin-webtestclient-issue]]
==== `WebTestClient` Type Inference Issue in Kotlin
@@ -968,17 +1025,24 @@ since it provides a workaround for the Kotlin issue with the Java API.
See also the related https://jira.spring.io/browse/SPR-16057[SPR-16057] issue.
[[kotlin-getting-started]]
== Getting Started
The easiest way to learn how to build a Spring application with Kotlin is to follow
https://spring.io/guides/tutorials/spring-boot-kotlin/[the dedicated tutorial].
=== `start.spring.io`
The easiest way to start a new Spring Framework project in Kotlin is to create a new Spring
Boot 2 project on https://start.spring.io/#!language=kotlin&type=gradle-project[start.spring.io].
=== Choosing the Web Flavor
Spring Framework now comes with two different web stacks: <<web#mvc, Spring MVC>> and
@@ -991,6 +1055,9 @@ Kotlin DSL.
For other use cases, especially if you are using blocking technologies such as JPA, Spring
MVC and its annotation-based programming model is the recommended choice.
[[kotlin-resources]]
== Resources
@@ -1004,6 +1071,8 @@ Kotlin and the Spring Framework:
* https://blog.jetbrains.com/kotlin/[Kotlin blog]
* https://kotlin.link/[Awesome Kotlin]
=== Examples
The following Github projects offer examples that you can learn from and possibly even extend:
@@ -1016,6 +1085,8 @@ The following Github projects offer examples that you can learn from and possibl
* https://github.com/sdeleuze/spring-kotlin-deepdive[spring-kotlin-deepdive]: A step-by-step migration guide for Boot 1.0 and Java to Boot 2.0 and Kotlin
* https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-kotlin-samples/spring-cloud-gcp-kotlin-app-sample[spring-cloud-gcp-kotlin-app-sample]: Spring Boot with Google Cloud Platform Integrations
=== Issues
The following list categorizes the pending issues related to Spring and Kotlin support:
@@ -1141,9 +1141,10 @@ request with a simple request parameter.
[.small]#<<web-reactive.adoc#webflux-multipart, WebFlux>>#
`MultipartResolver` from the `org.springframework.web.multipart` package is a strategy
for parsing multipart requests including file uploads. There is one implementation
based on https://commons.apache.org/proper/commons-fileupload[Commons FileUpload] and
another based on Servlet multipart request parsing.
for parsing multipart requests including file uploads. There is a container-based
`StandardServletMultipartResolver` implementation for Servlet multipart request parsing.
Note that the outdated `CommonsMultipartResolver` based on Apache Commons FileUpload is
not available anymore, as of Spring Framework 6.0 with its new Servlet 5.0+ baseline.
To enable multipart handling, you need to declare a `MultipartResolver` bean in your
`DispatcherServlet` Spring configuration with a name of `multipartResolver`.
@@ -1153,26 +1154,6 @@ content wraps the current `HttpServletRequest` as a `MultipartHttpServletRequest
provide access to resolved files in addition to exposing parts as request parameters.
[[mvc-multipart-resolver-commons]]
==== Apache Commons `FileUpload`
To use Apache Commons `FileUpload`, you can configure a bean of type
`CommonsMultipartResolver` with a name of `multipartResolver`. You also need to have
the `commons-fileupload` jar as a dependency on your classpath.
This resolver variant delegates to a local library within the application, providing
maximum portability across Servlet containers. As an alternative, consider standard
Servlet multipart resolution through the container's own parser as discussed below.
[NOTE]
====
Commons FileUpload traditionally applies to POST requests only but accepts any
`multipart/` content type. See the
{api-spring-framework}/web/multipart/commons/CommonsMultipartResolver.html[`CommonsMultipartResolver`]
javadoc for details and configuration options.
====
[[mvc-multipart-resolver-standard]]
==== Servlet Multipart Parsing
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.1-SNAPSHOT
version=6.0.2
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
@@ -49,7 +49,8 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector;
import org.springframework.core.KotlinReflectionParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
@@ -100,7 +101,7 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
private ConstraintValidatorFactory constraintValidatorFactory;
@Nullable
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
private ParameterNameDiscoverer parameterNameDiscoverer;
@Nullable
private Resource[] mappingLocations;
@@ -117,6 +118,13 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
private ValidatorFactory validatorFactory;
public LocalValidatorFactoryBean() {
if (KotlinDetector.isKotlinReflectPresent()) {
this.parameterNameDiscoverer = new KotlinReflectionParameterNameDiscoverer();
}
}
/**
* Specify the desired provider class, if any.
* <p>If not specified, JSR-303's default search mechanism will be used.
@@ -188,7 +196,10 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
/**
* Set the ParameterNameDiscoverer to use for resolving method and constructor
* parameter names if needed for message interpolation.
* <p>Default is a {@link org.springframework.core.DefaultParameterNameDiscoverer}.
* <p>Default is Hibernate Validator's own internal use of standard Java reflection,
* with an additional {@link KotlinReflectionParameterNameDiscoverer} if Kotlin
* is present. This may be overridden with a custom subclass or a Spring-controlled
* {@link org.springframework.core.DefaultParameterNameDiscoverer} if necessary,
*/
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,11 +30,13 @@ import org.springframework.lang.Nullable;
* {@link ParameterNameDiscoverer} implementation which uses Kotlin's reflection facilities
* for introspecting parameter names.
*
* Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to
* <p>Compared to {@link StandardReflectionParameterNameDiscoverer}, it allows in addition to
* determine interface parameter names without requiring Java 8 -parameters compiler flag.
*
* @author Sebastien Deleuze
* @since 5.0
* @see StandardReflectionParameterNameDiscoverer
* @see DefaultParameterNameDiscoverer
*/
public class KotlinReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
@@ -47,12 +47,21 @@ import org.springframework.util.ClassUtils;
* caches the ASM discovered information for each introspected Class, in a thread-safe
* manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
*
* <p>This class is deprecated in the 6.0 generation and scheduled for removal in 6.1
* since it is effectively superseded by {@link StandardReflectionParameterNameDiscoverer}.
* For the time being, this discoverer logs a warning every time it actually inspects a
* class file which is particularly useful for identifying remaining gaps in usage of
* the standard "-parameters" compiler flag, and also unintended over-inspection of
* e.g. JDK core library classes (which are not compiled with the "-parameters" flag).
*
* @author Adrian Colyer
* @author Costin Leau
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
* @since 2.0
* @see StandardReflectionParameterNameDiscoverer
* @see DefaultParameterNameDiscoverer
* @deprecated as of 6.0.1, in favor of {@link StandardReflectionParameterNameDiscoverer}
* (with the "-parameters" compiler flag)
*/
@@ -85,12 +94,7 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD
private String[] doGetParameterNames(Executable executable) {
Class<?> declaringClass = executable.getDeclaringClass();
Map<Executable, String[]> map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass);
String[] names = (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null);
if (names != null && logger.isWarnEnabled()) {
logger.warn("Using deprecated '-debug' fallback for parameter name resolution. " +
"Compile the affected code with '-parameters' instead: " + executable);
}
return names;
return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null);
}
/**
@@ -115,6 +119,10 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD
ClassReader classReader = new ClassReader(is);
Map<Executable, String[]> map = new ConcurrentHashMap<>(32);
classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0);
if (logger.isWarnEnabled()) {
logger.warn("Using deprecated '-debug' fallback for parameter name resolution. Compile the " +
"affected code with '-parameters' instead or avoid its introspection: " + clazz.getName());
}
return map;
}
catch (IOException ex) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,10 +26,15 @@ import org.springframework.lang.Nullable;
* {@link ParameterNameDiscoverer} implementation which uses JDK 8's reflection facilities
* for introspecting parameter names (based on the "-parameters" compiler flag).
*
* <p>This is a key element of {@link DefaultParameterNameDiscoverer} where it is being
* combined with {@link KotlinReflectionParameterNameDiscoverer} if Kotlin is present.
*
* @author Juergen Hoeller
* @since 4.0
* @see java.lang.reflect.Method#getParameters()
* @see java.lang.reflect.Parameter#getName()
* @see KotlinReflectionParameterNameDiscoverer
* @see DefaultParameterNameDiscoverer
*/
public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
@@ -442,7 +442,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
}
if (logger.isInfoEnabled()) {
logger.info("Starting \"system\" session, " + toString());
logger.info("Starting \"system\" session, " + this);
}
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.CONNECT);
@@ -699,7 +699,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
@Override
public void afterConnected(TcpConnection<byte[]> connection) {
if (logger.isDebugEnabled()) {
logger.debug("TCP connection opened in session=" + getSessionId());
logger.debug("TCP connection " + connection + " opened in session=" + getSessionId());
}
this.tcpConnection = connection;
connection.onReadInactivity(() -> {
@@ -978,7 +978,7 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler
this.tcpConnection = null;
if (conn != null) {
if (logger.isDebugEnabled()) {
logger.debug("Closing TCP connection in session " + this.sessionId);
logger.debug("Closing TCP connection " + conn + " in session " + this.sessionId);
}
conn.close();
}
@@ -81,4 +81,8 @@ public class ReactorNetty2TcpConnection<P> implements TcpConnection<P> {
this.completionSink.tryEmitEmpty();
}
@Override
public String toString() {
return "ReactorNetty2TcpConnection[inbound=" + this.inbound + "]";
}
}
@@ -80,4 +80,9 @@ public class ReactorNettyTcpConnection<P> implements TcpConnection<P> {
this.completionSink.tryEmitEmpty();
}
@Override
public String toString() {
return "ReactorNettyTcpConnection[inbound=" + this.inbound + "]";
}
}
@@ -56,8 +56,7 @@ public class MethodNotAllowedException extends ResponseStatusException {
}
this.method = method;
this.httpMethods = Collections.unmodifiableSet(new LinkedHashSet<>(supportedMethods));
getBody().setDetail(this.httpMethods.isEmpty() ?
getReason() : "Supported methods: " + this.httpMethods);
setDetail(this.httpMethods.isEmpty() ? getReason() : "Supported methods: " + this.httpMethods);
}
@@ -42,7 +42,7 @@ public class MissingRequestValueException extends ServerWebInputException {
this.name = name;
this.type = type;
this.label = label;
getBody().setDetail(getReason());
setDetail(getReason());
}
@@ -47,7 +47,7 @@ public class NotAcceptableStatusException extends ResponseStatusException {
public NotAcceptableStatusException(String reason) {
super(HttpStatus.NOT_ACCEPTABLE, reason, null, PARSE_ERROR_DETAIL_CODE, null);
this.supportedMediaTypes = Collections.emptyList();
getBody().setDetail("Could not parse Accept header.");
setDetail("Could not parse Accept header.");
}
/**
@@ -58,7 +58,7 @@ public class NotAcceptableStatusException extends ResponseStatusException {
"Could not find acceptable representation", null, null, new Object[] {mediaTypes});
this.supportedMediaTypes = Collections.unmodifiableList(mediaTypes);
getBody().setDetail("Acceptable representations: " + mediaTypes + ".");
setDetail("Acceptable representations: " + mediaTypes + ".");
}
@@ -23,8 +23,9 @@ import org.springframework.lang.Nullable;
import org.springframework.web.ErrorResponseException;
/**
* Subclass of {@link ErrorResponseException} that accepts a "reason" and maps
* it to the "detail" property of {@link org.springframework.http.ProblemDetail}.
* Subclass of {@link ErrorResponseException} that accepts a "reason", and by
* default maps that to the {@link ErrorResponseException#setDetail(String) "detail"}
* of the {@code ProblemDetail}.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
@@ -93,6 +94,7 @@ public class ResponseStatusException extends ErrorResponseException {
super(status, ProblemDetail.forStatus(status), cause, messageDetailCode, messageDetailArguments);
this.reason = reason;
setDetail(reason);
}
@@ -40,7 +40,7 @@ public class UnsatisfiedRequestParameterException extends ServerWebInputExceptio
super(initReason(conditions, params), null, null, null, new Object[] {conditions});
this.conditions = conditions;
this.requestParams = params;
getBody().setDetail("Invalid request parameters.");
setDetail("Invalid request parameters.");
}
private static String initReason(List<String> conditions, MultiValueMap<String, String> queryParams) {
@@ -62,7 +62,7 @@ public class UnsupportedMediaTypeStatusException extends ResponseStatusException
this.supportedMediaTypes = Collections.emptyList();
this.bodyType = null;
this.method = null;
getBody().setDetail("Could not parse Content-Type.");
setDetail("Could not parse Content-Type.");
}
/**
@@ -316,7 +316,7 @@ public class ErrorResponseExceptionTests {
ServerErrorException ex = new ServerErrorException("Failure", null);
assertStatus(ex, HttpStatus.INTERNAL_SERVER_ERROR);
assertDetail(ex, null);
assertDetail(ex, "Failure");
assertDetailMessageCode(ex, null, new Object[] {ex.getReason()});
assertThat(ex.getHeaders()).isEmpty();