WebTestClient 在 Spring Boot 2.4.0-M3 上获得 404,而在 2.4.0-M2 上工作正常

WebTestClient gets 404 on Spring Boot 2.4.0-M3 while works fine on 2.4.0-M2

我的测试可以在 Spring 2.4.0-M2 上正常工作,但在升级到 2.4.0-M3 后它会中断 - returns 404 对于已注册的路由。

我的应用程序:

@SpringBootApplication(proxyBeanMethods = false)
class ExampleApp

fun main(args: Array<String>) {
    runApplication<ExampleApp>(
        init = {
            addInitializers(BeansInitializer())
        },
        args = args
    )
}

豆类:

class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
    @Suppress("LongMethod")
    override fun initialize(applicationContext: GenericApplicationContext) {
        beans {
            bean {
                router {
                    "/routes".nest {
                        GET("/{id}") { ServerResponse.ok().bodyValue(Foo("ok")) }
                        POST("/") { ServerResponse.ok().bodyValue(Foo("ok")) }
                    }
                }
            }
        }
            .initialize(applicationContext)
    }
}

data class Foo(val status: String)

我的测试:

@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    classes = [
        ExampleApp::class
    ]
)
class FailingTest @Autowired constructor(
    context: ApplicationContext,
) {
    val webTestClient: WebTestClient = WebTestClient.bindToApplicationContext(context)
        .configureClient()
        .build()

    @Test
    fun `should interact with routes`() {
        webTestClient
            .post()
            .uri("/routes")
            .bodyValue(SampleBody("123"))
            .exchange()
            .expectStatus()
            .isOk // returns 404 on 2.4.0-M3 / passes on 2.4.0-M2
    }

    data class SampleBody(val id: String)
}

测试application.yml

context:
  initializer:
    classes: com.example.BeansInitializer

2.4.0-M3 测试失败并显示以下消息:

java.lang.AssertionError: Status expected:<200 OK> but was:<404 NOT_FOUND>

2.4.0-M2 他们通过了。

这些版本有什么变化吗?或者这是一个错误?

您看到的行为变化是由于 Spring Framework 在 5.3 开发过程中的改进。

默认情况下,Spring 框架将匹配一个可选的尾随路径分隔符 (/)。这个可选的 / 应该是在你的路由中指定的路径之外的。

您有两条路线:

  • GET /routes/{id}
  • POST /routes/

支持可选的尾随路径分隔符意味着您可以向 /routes/56/ 发出 get 请求(一个额外的尾随 /),但这不应该意味着您可以向 /routes/56/ 发出请求POST /routes(删除尾随 /)。

如果您希望能够向 /routes/routes/ 发出 POST 请求,您应该将路由定义为 /routes:

beans {
    bean {
        router {
            "/routes".nest {
                GET("/{id}") { ServerResponse.ok().bodyValue(Foo("ok")) }
                POST("") { ServerResponse.ok().bodyValue(Foo("ok")) }
            }
        }
    }
}