Flutter can't connect to Rails API only properly

1,100

Ok, I actually found a problem (and a solution).

For some weird reason, Rails requires the data to be posted explicitly as a "Content-type": "application/json".

So it's also needed to set the request headers, and pass them into the http.post method.

Future<HeadUser> loginUser(String url, {Map body}) async {
  Map<String, String> headers = {"Content-type": "application/json"};
  return http.post(url, headers: headers, body: json.encode(body)).then((http.Response response) {
    final int statusCode = response.statusCode;
    if (statusCode < 200 || statusCode > 400 || statusCode == null) {
      throw new Exception("Error while posting data");
    }
    return HeadUser.fromJson(json.decode(response.body));
  });
Share:
1,100
mutantkeyboard
Author by

mutantkeyboard

I've been programming since I was 15. Started with C++-98, and later moved to a various different technologies and methodologies of development. I also have a decent knowledge of JS, and the frameworks associated with it (Vue, React, Angular), as well as Dart &amp; Flutter. These days I mostly work with Ruby/Go + Flutter.

Updated on December 13, 2022

Comments

  • mutantkeyboard
    mutantkeyboard over 1 year

    Ok guys, here is my problem.

    I have a standard Rails 5 API only setup.

    My user controller is pretty much standard one:

    # frozen_string_literal: true
    
    class UsersController < ApplicationController
      before_action :set_user, only: %i[show update destroy]
    
      # GET /users
      def index
        @users = User.all
    
        render json: @users
      end
    
      # GET /users/1
      def show
        render json: @user
      end
    
      # POST /users
      def create
        @user = User.new(user_params)
    
    
        if @user.save
          render json: @user, status: :created, location: @user
          UserNotifierMailer.send_signup_email(@user).deliver      
        else
          render json: @user.errors, status: :unprocessable_entity
        end
      end
    
      # PATCH/PUT /users/1
      def update
        if @user.update(user_params)
          render json: @user
        else
          render json: @user.errors, status: :unprocessable_entity
        end
      end
    
      # DELETE /users/1
      def destroy
        @user.destroy
      end
    
      private
    
      # Use callbacks to share common setup or constraints between actions.
      def set_user
        @user = User.find(params[:id])
      end
    
      # Only allow a trusted parameter "white list" through.
      def user_params
        params.require(:user).permit(:username, :password, :email)
      end
    end
    

    Now when I post data to the server from Postman it works flawlessly, and it registers the user just fine.

    Here is what I have from Postman: Screenshot

    On the other hand, I'm using the Flutter application, and I'm trying to register.

    Here is Flutter model.

    import 'dart:convert';
    import 'package:http/http.dart' as http;
    
    class HeadUser {
      User _user;
    
      HeadUser({User user}) {
        this._user = user;
      }
    
      HeadUser.fromJson(Map<String, dynamic> json) {
        _user = json['user'] != null ? new User.fromJson(json['user']) : null;
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        if (this._user != null) {
          data['user'] = this._user.toJson();
        }
        return data;
      }
    }
    
    class User {
      String _username;
      String _email;
      String _password;
      int _id;
      DateTime _createdAt;
      DateTime _updatedAt;
    
      User({int id, DateTime createdAt, DateTime updatedAt, String username, String email, String password}) {
        this._username = username;
        this._email = email;
        this._password = password;
        this._id = id;
        this._createdAt = createdAt;
        this._updatedAt = updatedAt;
      }
    
      factory User.fromJson(Map<String, dynamic> json) {
        return User(
        id: json['id'],
        username: json['username'],
        email: json['email'],
        createdAt: json['created_at'],
        updatedAt: json['updated_at'],
        );
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['username'] = this._username;
        data['email'] = this._email;
        data['password'] = this._password;
        return data;
      }
    }
    
    Future<HeadUser> loginUser(String url, {Map body}) async {
      return http.post(url, body: body).then((http.Response response) {
        final int statusCode = response.statusCode;
        if (statusCode < 200 || statusCode > 400 || statusCode == null) {
          throw new Exception("Error while posting data");
        }
        return HeadUser.fromJson(json.decode(response.body));
      });
    
    }
    

    And my call for this one looks like:

     onPressed: () async {
                              if (_formKey.currentState.validate())
                              {
                                HeadUser usr = new HeadUser(user: User(
                                  email: this.emailEditingController.text.trim(),
                                  password: this.passwordEditingController.text.trim(),
                                  username: this.usernameEditingController.text.trim()
                                ));
    
    
                                HeadUser response = await loginUser(REGISTRATION_URL, body: usr.toJson());
    

    But when I do that, I get an error in my Flutter application like I'm having a "user" parameter missing, though when I print it out it shows the exact structure, but I get this. screenshot 2

    In this case, request is not even fired, so the server doesn't get hit.

    However if I try to do json.encode(body) in a loginUser method, then my response from server is:

    screenshot 3

    And of course Flutter complains:

    screenshot 4

    What am I doing wrong here?

    • A moskal escaping from Russia
      A moskal escaping from Russia over 4 years
      The params Rails receives must be in the form { user: { id: ..., name: ... } }, what you're sending now is probably like { id: ..., name: ... }.
    • mutantkeyboard
      mutantkeyboard over 4 years
      @SebastianPalma thank you for the tip. It wasn't the issue, but the request headers.
  • Kamal Panhwar
    Kamal Panhwar about 3 years
    I have seen many response, but this header things was the issue! thanks after 7 hours, I got your response and solved issue.