Flutter - Handle status code 302 in POST request
Asked Answered
C

8

19

I'm trying to send a post request in Flutter with DIO package.

Here is the request:

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);
}

After the request i get:

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>

From this request i only need to get the sessionid cookie, but the function stop with unhandled exception.

Calcify answered 27/9, 2018 at 12:54 Comment(1)
Check my answer : https://mcmap.net/q/456245/-unable-to-add-header-for-post-method-in-dio-in-flutter That can help too!Busyness
C
28

I solved this way:

Add followRedirects: false and validateStatus: (status) { return status < 500;} to the request. Like this:

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

This way you can get from the 302 every headers and other.

Calcify answered 27/9, 2018 at 17:40 Comment(2)
it can be solved by adding Accept type in header as application/jsonFabiano
"Accept" : "application/json" solved my issue. Thank youTori
F
7

i got a similar problem and i solved it with adding header with "Accept":"application/json" . henceforth it will only return json data otherwise it will prompt to redirect with html url.

Fabiano answered 26/3, 2021 at 11:4 Comment(0)
R
6

The Dart HTTP client won't follow redirects for POSTs unless the response code is 303. It follows 302 redirects for GET or HEAD.

You could see if you can stop the server sending the redirect in response to a (presumably) valid login request, and send a 200 instead.

Or you could try sending the login request as a GET by encoding the form fields into the URL, for example:

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

You would have to URL encode any special characters in the parameters. Presumably, you'll want to use HTTPS too.

Finally, is the URL meant to end with /? It might be worth trying /accounts/login.

Razor answered 27/9, 2018 at 14:32 Comment(5)
Thank you. But I only need the "sessionid" cookie from this response.Calcify
You can't access anything from a 302 response, except the redirect location (and that's handled automatically by the client). The client, correctly, won't follow a redirect to a POST for various reasons.Razor
Ok thank you! Then I have only the 2 possibility you wrote?Calcify
Yes, check the URL first, then try GET, and lastly see if the server can send 200 instead of a redirect.Razor
Just to explain why this happens. The http package strictly follows the HTTP specification, and by it redirect on POST shouldn't use 302 but 303: developer.mozilla.org/en-US/docs/Web/HTTP/…Carcinogen
C
2

Redirections for 302 are made in response to GET or HEAD requests, never for POST. Sometimes server sends 302 in response to POST (that was in my case). In this case Dio throws exception you can catch - remember to check if server status code is 302 or maybe it's another error.

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
     }
Cooe answered 9/3, 2019 at 20:18 Comment(0)
M
1

I'm trying to use this to a webscraping... don't ask me why lol. I came from python/golang and I've already tried the http package, however i recommend you to use the dio package.

With dio I'm doing the following:

      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);

Note that it's just a simple approach. You can change it according with you needs.

Mangonel answered 1/9, 2021 at 0:25 Comment(0)
K
1

I also had this problem, I sent a POST api request using dio library and it returned an error (The request returned an invalid status code of 302)

I issued an identical request using Postman, resulting in a successful response with a status code 200. However, when I attempted the same request in Flutter, it produced an error with a returned status code of 302.

In my case the expected response was the html code of a site I'm being redirected to.


Well, let me explain the problem first:

The HTTP 302 error occurs when the requested resource has been temporarily moved to a new location. Hence, the system automatically redirects visitors to a new URL that has the resource.

The HTTP redirect starts when the web server hosting the content returns a 3xx status code and a location header that holds the new URL. Once the web browser receives this response, it automatically loads the new URL instead of displaying the 404 error not found error.

Source: hostinger.com article


Conclusion/data:

  • When you receive a 302 error in Flutter using the Dio package, it means that the server is trying to redirect you to a different site.
  • The default behavior of the Dio package is to automatically follow the redirect via the followRedirects property and get the HTML response from the redirected site. But I think it failed to redirect since the request was a POST, since

only "GET" and "HEAD" requests in HttpClientRequest will auto-redirect

as mentioned in dart.dev.


The solution:

  1. Prevent Dio from proceeding with redirects "automatically".
  2. Fetch the link of the site you are supposed to be redirected to (instead of returning it directly in response).
  3. View the website using any webView library.

Solution steps:

  1. Set the FollowRedirects and maxRedirects options in the Options Dio request like this, to prevent automatic redirects from continuing.
final response = await dioClient.post(
        // some code
        options: Options(
          followRedirects: false, // default is true, change to false
          maxRedirects: 0, // set to 0
        ),
      );
  1. Set validateStatus as follows:
Options(
   validateStatus: (status) => status! < 500, // or (=> status != null & status < 500 ? true : false), to avoid null exception
),

It is preferable to set these two previous options only for this request without affecting the rest of the requests.

  1. Get the URL of the site you are supposed to be redirected to via response.headers['location'] like this:
      String? redirectUrl;
      if(response.statusCode == 302){ // check if status code equals 302, then get the redirect url
        redirectUrl = '${response.headers['location']}'; // get the redirect URL
      }
  1. View the website using any webView package, like webview_flutter.

The full code would be like:

final response = await dioClient.post(
        // URI here
        data: // data here
        options: Options(
          followRedirects: false,
          maxRedirects: 0,
          validateStatus: (status) => status! < 500,
        ),
      );

      String? redirectUrl;
      if(response.statusCode == 302){ // check if status code equals 302, then get the redirect url
        redirectUrl = '${response.headers['location']}';
        debugPrint('is redirect: ${response.isRedirect}');
        log('redirect location: $redirectUrl');
      } else if (response.statusCode == 200){
        // some code here
      }
Killdeer answered 24/8, 2023 at 21:2 Comment(0)
P
0

in my case, this problem was solved by send the cookie with the header in the post method

and the problem is the API was response to me with HTML login page rather than JSON data.

and you will find the cookie key in the response header when you perform a si/log - in

and the status error code was 302

Pinkeye answered 10/9, 2019 at 10:16 Comment(0)
O
0

the solution for me was changing the url form http to https because the server was trying to redirects http request to https .

Oratorian answered 4/7 at 5:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.