当前位置: 首页 > 测试知识 > Gatling重定向处理:disableFollowRedirect和自动跳转控制
Gatling重定向处理:disableFollowRedirect和自动跳转控制
2025-12-30 作者cwb 浏览次数13

Gatling性能测试的重定向处理是为了控制测试流程、保证测试意图准确性和测量精度。Gatling默认自动跟随重定向,但为了满足复杂的测试情形(如多步骤认证、精确追踪跳转或测试特定重定向思路),必须能够手动控制这一行为。控制参数即为disableFollowRedirect。


一、自动跟随和手动控制


默认行为(自动跟随)

默认情况下,Gatling的HTTP协议会像浏览器一样,自动处理 3xx 状态码(如301、302、307)的重定向。测试脚本中只需定义一个请求,后续跳转请求由Gatling自动执行,只将最后一个非重定向响应的结果(状态码、响应时间等)记录到报告中。

优点:脚本编写简单,适合模拟绝大多数常规浏览行为。

缺点:丢失了中间跳转步骤的详细性能数据(如每个重定向请求的延迟),无法对中间状态进行断言,难以构造复杂的多步骤交互。


手动控制(禁用自动跟随)

通过在协议或单个请求方面调用 .disableFollowRedirect,Gatling将停止自动跳转。当收到 3xx 响应时,会立即停止,并将该重定向响应本身作为最后结果返回给脚本。

作用:获得了完整的控制权。可以检查重定向响应的头部(如Location)、状态码,并据此决定下一步操作(如下一个请求的目标、是不是需要携带特定参数)。


二、disableFollowRedirect 的应用情形

在以下情形中,禁用自动重定向是必须或最好选择:

精确性能监控:需要单独测量重定向步骤本身的延迟(如,CDN或负载均衡器的重定向时间)。禁用后重定向请求将作为独立事务被记录和报告。

多步骤认证流程:如OAuth 2.0授权码流程,需要从A站跳转到B站登录,再携带code跳回A站。必须手动处理中间的重定向响应来提取参数。

测试特定重定向思路:需要证实服务器返回的是302(临时)还是301(永久),或者检查Location头的值是不是正确。

处理重定向链中的特定步骤:在长链条中,可能需要在某个中间步骤注入或修改参数(如会话ID),再发起下一个跳转。

避免重定向的循环:当怀疑存在配置错误导致的重定向无限循环时,禁用后可以快速定位问题,避免压测资源被无限消耗。


配置方法:


全局禁用在协议方面-适用于整个测试套件都需要精细控制的情况。


scala

val httpProtocol = http

  .disableFollowRedirect // 为所有请求关闭自动重定向


局部禁用在请求方面-灵活性最高只对需要手动处理的请求生效。


scala

exec(http("请求:获取登录页")

  .get("/login")

  .check(status.is(200)) // 先检查登录页是不是正常

)

.exec(http("提交登录表单(禁用自动跳转)")

  .post("/login")

  .formParam("username", "user")

  .formParam("password", "pass")

  .disableFollowRedirect // 仅对此请求禁用

  .check(status.is(302)) // 证实服务器确实发出了重定向指令

  .check(header("Location").saveAs("redirectTarget")) // 提取重定向地址

)

.exec(http("跟随重定向")

  .get("${redirectTarget}") // 手动发起请求到提取的地址

  .check(status.is(200))

)


三、示例模拟OAuth 2.0授权码

以下是一个禁用自动重定向来精确模拟OAuth登录的示例:


scala

import io.gatling.core.Predef._

import io.gatling.http.Predef._

import scala.concurrent.duration._


class OAuthRedirectSimulation extends Simulation {


  val httpProtocol = http

    .baseUrl("https://app.example.com")

    .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")


  val scn = scenario("OAuth 2.0 Authorization Code Flow")

    // 1. 应用将用户重定向到OAuth服务器

    .exec(

      http("Redirect to OAuth Provider")

        .get("/auth/oauth-authorize")

        .queryParam("response_type", "code")

        .queryParam("client_id", "myClientId")

        .queryParam("redirect_uri", "https://app.example.com/callback")

        .queryParam("state", "RANDOM_STATE")

        .disableFollowRedirect // 禁用,以获取OAuth服务器的登录页地址

        .check(

          status.is(302),

          header("Location").saveAs("oauthLoginUrl")

        )

    )

    // 2. 手动请求OAuth服务器的登录页(模拟用户在此输入证据)

    .exec(

      http("Load OAuth Login Page")

        .get("${oauthLoginUrl}")

        .check(

          // 一般需要从登录页HTML中提取CSRF Token

          css("input[name='csrf']", "value").saveAs("csrfToken")

        )

    )

    // 3. 提交登录表单到OAuth服务器(再次禁用自动跳转,以获取授权码)

    .exec(

      http("Submit Login to OAuth Provider")

        .post("${oauthLoginUrl}")

        .formParam("username", "test_user")

        .formParam("password", "test_pass")

        .formParam("csrf", "${csrfToken}")

        .disableFollowRedirect // 重点:禁用以拦截携带code的重定向

        .check(

          status.is(302),

          header("Location").saveAs("redirectUriWithCode")

        )

    )

    // 4. 从Location头中分析授权码

    .exec(session => {

      val location = session("redirectUriWithCode").as[String]

      // 简单示例:从查询参数中提取code

      val code = location.split("code=")(1).split("&")(0)

      session.set("authorizationCode", code)

    })

    // 5. 应用用授权码向OAuth服务器请求访问令牌(后端通信,无重定向)

    .exec(

      http("Exchange Code for Access Token")

        .post("https://oauth.provider.com/token")

        .formParam("grant_type", "authorization_code")

        .formParam("code", "${authorizationCode}")

        .formParam("redirect_uri", "https://app.example.com/callback")

        .formParam("client_id", "myClientId")

        .formParam("client_secret", "myClientSecret")

        .check(jsonPath("$.access_token").saveAs("accessToken"))

    )

    // 6. 使用令牌访问受保护资源

    .exec(

      http("Access Protected Resource")

        .get("/api/data")

        .header("Authorization", "Bearer ${accessToken}")

    )


  setUp(

    scn.inject(atOnceUsers(1))

  ).protocols(httpProtocol)

}


四、调试建议

方法选择:除非有确定的精细控制需求,否则保持默认的自动重定向,这样简单能模拟大多数用户行为。

结合检查点:禁用自动重定向后,必须添加检查点来证实重定向响应(如status.is(302),header("Location").exists),否则脚本可能因意外响应而失败。

性能报告解读:启用disableFollowRedirect后,每个独立的请求(包括重定向) 都会在Gatling报告中生成一条记录。这能清晰分析每个跳转步骤的耗时。

处理相对途径:手动从Location头提取URL时,注意可能是相对途径。Gatling的.get()方法支持相对途径,但最好方式是使用完整的URL或保证基础URL已正确设置。

调试:在开发脚本阶段,可以临时添加.check(header("Location").transform(location => {println(s"Redirecting to: $location"); location}))来打印重定向地址,辅助调试。

文章标签: 软件测试 测试工具
咨询软件测试