@ -21,13 +21,11 @@ import java.util.Collections;
import java.util.Set ;
import java.util.Set ;
import jakarta.servlet.http.HttpServletRequest ;
import jakarta.servlet.http.HttpServletRequest ;
import jakarta.servlet.http.HttpServletResponse ;
import org.jspecify.annotations.Nullable ;
import org.jspecify.annotations.Nullable ;
import org.springframework.util.Assert ;
import org.springframework.util.Assert ;
import org.springframework.util.StringUtils ;
import org.springframework.util.StringUtils ;
import org.springframework.web.accept.ApiVersionStrategy ;
import org.springframework.web.accept.ApiVersionStrategy ;
import org.springframework.web.accept.InvalidApiVersionException ;
import org.springframework.web.accept.NotAcceptableApiVersionException ;
import org.springframework.web.accept.NotAcceptableApiVersionException ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.servlet.HandlerMapping ;
import org.springframework.web.servlet.HandlerMapping ;
@ -42,21 +40,12 @@ import org.springframework.web.servlet.HandlerMapping;
* /
* /
public final class VersionRequestCondition extends AbstractRequestCondition < VersionRequestCondition > {
public final class VersionRequestCondition extends AbstractRequestCondition < VersionRequestCondition > {
private static final String VERSION_ATTRIBUTE_NAME = VersionRequestCondition . class . getName ( ) + ".VERSION" ;
private static final String NO_VERSION_ATTRIBUTE = "NO_VERSION" ;
private static final ApiVersionStrategy NO_OP_VERSION_STRATEGY = new NoOpApiVersionStrategy ( ) ;
private final @Nullable String versionValue ;
private final @Nullable String versionValue ;
private final @Nullable Comparable < ? > version ;
private final @Nullable Comparable < ? > version ;
private final boolean baselineVersion ;
private final boolean baselineVersion ;
private final ApiVersionStrategy versionStrategy ;
private final Set < String > content ;
private final Set < String > content ;
@ -67,14 +56,12 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
public VersionRequestCondition ( @Nullable String version , @Nullable ApiVersionStrategy strategy ) {
public VersionRequestCondition ( @Nullable String version , @Nullable ApiVersionStrategy strategy ) {
if ( StringUtils . hasText ( version ) ) {
if ( StringUtils . hasText ( version ) ) {
Assert . isTrue ( strategy ! = null , "ApiVersionStrategy is required for mapping by version" ) ;
Assert . isTrue ( strategy ! = null , "ApiVersionStrategy is required for mapping by version" ) ;
this . versionStrategy = strategy ;
this . baselineVersion = version . endsWith ( "+" ) ;
this . baselineVersion = version . endsWith ( "+" ) ;
this . versionValue = updateVersion ( version , this . baselineVersion ) ;
this . versionValue = updateVersion ( version , this . baselineVersion ) ;
this . version = this . versionS trategy. parseVersion ( this . versionValue ) ;
this . version = s trategy. parseVersion ( this . versionValue ) ;
this . content = Set . of ( version ) ;
this . content = Set . of ( version ) ;
}
}
else {
else {
this . versionStrategy = ( strategy ! = null ? strategy : NO_OP_VERSION_STRATEGY ) ;
this . versionValue = null ;
this . versionValue = null ;
this . version = null ;
this . version = null ;
this . baselineVersion = false ;
this . baselineVersion = false ;
@ -111,37 +98,19 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
@Override
@Override
public @Nullable VersionRequestCondition getMatchingCondition ( HttpServletRequest request ) {
public @Nullable VersionRequestCondition getMatchingCondition ( HttpServletRequest request ) {
Comparable < ? > requestVersion = ( Comparable < ? > ) request . getAttribute ( VERSION_ATTRIBUTE_NAME ) ;
Comparable < ? > requestVersion = ( Comparable < ? > ) request . getAttribute ( HandlerMapping . API_VERSION_ATTRIBUTE ) ;
if ( requestVersion = = null ) {
String value = this . versionStrategy . resolveVersion ( request ) ;
requestVersion = ( value ! = null ? parseVersion ( value ) : this . versionStrategy . getDefaultVersion ( ) ) ;
this . versionStrategy . validateVersion ( requestVersion , request ) ;
requestVersion = ( requestVersion ! = null ? requestVersion : NO_VERSION_ATTRIBUTE ) ;
request . setAttribute ( VERSION_ATTRIBUTE_NAME , requestVersion ) ;
}
if ( this . version = = null | | requestVersion = = NO_VERSION_ATTRIBUTE ) {
if ( this . version = = null | | requestVersion = = null ) {
return this ;
return this ;
}
}
request . setAttribute ( HandlerMapping . API_VERSION_ATTRIBUTE , requestVersion ) ;
// Always use a baseline match here in order to select the highest version (baseline or fixed)
// The fixed version match is enforced at the end in handleMatch()
// At this stage, match all versions as baseline versions.
// Strict matching for fixed versions is enforced at the end in handleMatch.
int result = compareVersions ( this . version , requestVersion ) ;
int result = compareVersions ( this . version , requestVersion ) ;
return ( result < = 0 ? this : null ) ;
return ( result < = 0 ? this : null ) ;
}
}
private Comparable < ? > parseVersion ( String value ) {
try {
return this . versionStrategy . parseVersion ( value ) ;
}
catch ( Exception ex ) {
throw new InvalidApiVersionException ( value , null , ex ) ;
}
}
@SuppressWarnings ( "unchecked" )
@SuppressWarnings ( "unchecked" )
private < V extends Comparable < V > > int compareVersions ( Object v1 , Object v2 ) {
private < V extends Comparable < V > > int compareVersions ( Object v1 , Object v2 ) {
return ( ( V ) v1 ) . compareTo ( ( V ) v2 ) ;
return ( ( V ) v1 ) . compareTo ( ( V ) v2 ) ;
@ -178,7 +147,7 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
* /
* /
public void handleMatch ( HttpServletRequest request ) {
public void handleMatch ( HttpServletRequest request ) {
if ( this . version ! = null & & ! this . baselineVersion ) {
if ( this . version ! = null & & ! this . baselineVersion ) {
Comparable < ? > version = ( Comparable < ? > ) request . getAttribute ( VERSION_ATTRIBUTE_NAM E ) ;
Comparable < ? > version = ( Comparable < ? > ) request . getAttribute ( HandlerMapping . API_ VERSION_ATTRIBUTE) ;
Assert . state ( version ! = null , "No API version attribute" ) ;
Assert . state ( version ! = null , "No API version attribute" ) ;
if ( ! this . version . equals ( version ) ) {
if ( ! this . version . equals ( version ) ) {
throw new NotAcceptableApiVersionException ( version . toString ( ) ) ;
throw new NotAcceptableApiVersionException ( version . toString ( ) ) ;
@ -186,31 +155,4 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
}
}
}
}
private static final class NoOpApiVersionStrategy implements ApiVersionStrategy {
@Override
public @Nullable String resolveVersion ( HttpServletRequest request ) {
return null ;
}
@Override
public String parseVersion ( String version ) {
return version ;
}
@Override
public void validateVersion ( @Nullable Comparable < ? > requestVersion , HttpServletRequest request ) {
}
@Override
public @Nullable Comparable < ? > getDefaultVersion ( ) {
return null ;
}
@Override
public void handleDeprecations ( Comparable < ? > version , HttpServletRequest request , HttpServletResponse response ) {
}
}
}
}