Flutter can't connect to Rails API only properly
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));
});
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 & Flutter. These days I mostly work with Ruby/Go + Flutter.
Updated on December 13, 2022Comments
-
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:
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.
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 aloginUser
method, then my response from server is:And of course Flutter complains:
What am I doing wrong here?
-
A moskal escaping from Russia over 4 yearsThe params Rails receives must be in the form
{ user: { id: ..., name: ... } }
, what you're sending now is probably like{ id: ..., name: ... }
. -
mutantkeyboard over 4 years@SebastianPalma thank you for the tip. It wasn't the issue, but the request headers.
-
-
Kamal Panhwar about 3 yearsI have seen many response, but this header things was the issue! thanks after 7 hours, I got your response and solved issue.