Flutter - 在 POST 请求中处理状态代码 302

Flutter - Handle status code 302 in POST request

我正在尝试使用 DIO 包在 Flutter 中发送 post 请求。

请求如下:

getSessionId() async {

  var csrf = await getCsrftoken();

  var dio = new Dio(new Options(
      baseUrl: "http://xxxxxxx/accounts/login/",
      connectTimeout: 5000,
      receiveTimeout: 100000,
      // 5s
      headers: {
        'Cookie': "csrftoken=" + csrf
      },
      contentType: ContentType.JSON,
      // Transform the response data to a String encoded with UTF8.
      // The default value is [ResponseType.JSON].
      responseType: ResponseType.PLAIN
  ));

  var response;
  response = await dio.post("http://xxxxxxx/accounts/login/",
    data: {
      "username": "xxxxx",
      "password": "xxxxx",
      "csrfmiddlewaretoken" : csrf
    },
    options: new Options(
        contentType: ContentType.parse("application/x-www-form-urlencoded")),
  );

  print("StatusCode: ");
  print(response.statusCode);
  print("Response cookie: ");   //THESE ARE NOT PRINTED
  print(response.headers);
}

请求后我得到:

E/flutter ( 4567): [ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
    E/flutter ( 4567): DioError [DioErrorType.RESPONSE]: Http status error [302]
    E/flutter ( 4567): #0      getSessionId (file:///C:/get_order/lib/main.dart:36:14)
    E/flutter ( 4567): <asynchronous suspension>

根据这个请求,我只需要获取 sessionid cookie,但该函数因未处理的异常而停止。

除非响应代码为 303,否则 Dart HTTP 客户端不会 follow redirects POST。它遵循 GET 或 HEAD 的 302 重定向。

您可以查看是否可以阻止服务器发送重定向以响应(大概)有效的登录请求,而是发送 200。

或者您可以尝试通过将表单字段编码到 URL 中以 GET 方式发送登录请求,例如:

http://xxxxxxx/accounts/login/?username=xxxx&password=yyyy&csrfmiddlewaretoken=zzzz

您必须 URL 对参数中的任何特殊字符进行编码。据推测,您也想使用 HTTPS。

最后,URL是不是要以/结尾?可能值得一试 /accounts/login.

我是这样解决的:

在请求中添加 followRedirects: falsevalidateStatus: (status) { return status < 500;}。像这样:

var response = await Dio().post("http://myurl",
    data: requestBody,
    options: Options(
        followRedirects: false,
        validateStatus: (status) { return status < 500; }
    ),
);

这样你就可以从302headers等得到。

302 的重定向是为了响应 GET 或 HEAD 请求,而不是 POST。有时服务器发送 302 以响应 POST(在我的情况下)。在这种情况下,Dio 会抛出您可以捕捉到的异常 - 请记住检查服务器状态代码是否为 302 或者可能是另一个错误。

try{
    await dio.post( _urlLogin,
      data:{...},
      options: Options(
        contentType: ContentType.parse("application/x-www-form-urlencoded"),          
      )
  );
}on DioError catch(error){
    if(error.response.statusCode == 302){
    // do your stuff here
     }

在我的例子中,通过在 post 方法

中发送带有 header 的 cookie 解决了这个问题

问题是 API 是用 HTML 登录页面而不是 JSON 数据对我的回应。

当您在

中执行 si/log 时,您将在响应 header 中找到 cookie 密钥

状态错误代码为 302

我遇到了类似的问题,我通过添加 header 和 "Accept":"application/json" 解决了这个问题。从今以后它只会 return json 数据否则它会提示重定向 html url.

我正在尝试将其用于 webscraping...别问我为什么,哈哈。我来自 python/golang 并且我已经尝试过 http 包,但是我建议你使用 dio 包。

对于 dio,我正在执行以下操作:

      Scrapers.client = Dio();
      // create a local cookie to handle with sites that force you to use it
      var cookieJar = CookieJar();
      // assign middlewares to be "session like"
      Scrapers.client?.interceptors.add(CookieManager(cookieJar));
      // assign a logging middleware
      Scrapers.client?.interceptors.add(LogInterceptor(responseBody: false));
      // set the default content type to this client
      Scrapers.client?.options.contentType = Headers.formUrlEncodedContentType;


...

  static Future<Response> handleRedirects(Response r, int statusCode) async {
    var redirects = 0;
    while (r.statusCode == statusCode) {
      print("redirect #${redirects++}");
      final redirecturl = r.headers['location']![0];
      r = await Scrapers.client!.post(redirecturl,
          options: Options(
            followRedirects: false,
            validateStatus: (status) {
              return status! < 500;
          }));
    }
    return r;
  }

...


    Response r = await Scrapers.client!.post(url,
        data: payload,
        options: Options(
            followRedirects: false,
            validateStatus: (status) {
              return status! < 500;
            }));
    r = await Scrapers.handleRedirects(r, 302);

注意这只是一个简单的方法。您可以根据需要更改它。