使用Android Retrofit进行POST请求

23

我刚接触安卓编程并正在使用Retrofit。我已经对这个主题进行了大量研究,但是还没有找到符合我的需求的解决方案。我正在使用我们的API并尝试进行POST请求。我成功地通过以下非Retrofit代码实现了这一点:

    private class ProcessLogin extends AsyncTask<Void, String, JSONObject> {
    private ProgressDialog pDialog;
    String email,password;

    protected void onPreExecute() {
        super.onPreExecute();
        inputEmail = (EditText) findViewById(R.id.email);
        inputPassword = (EditText) findViewById(R.id.password);
        email = inputEmail.getText().toString();
        password = inputPassword.getText().toString();
        pDialog = new ProgressDialog(LoginActivity.this);
        pDialog.setTitle("Contacting Servers");
        pDialog.setMessage("Logging in ...");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(true);
        pDialog.show();
    }

    protected JSONObject doInBackground(Void... params) {
        HttpURLConnection connection;
        OutputStreamWriter request = null;
        URL url = null;   
        String response = null;         
        String parameters = "username="+email+"&password="+password;  
        try
        {
            url = new URL("http://.../api/login");
            connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestMethod("POST");    

            request = new OutputStreamWriter(connection.getOutputStream());
            request.write(parameters);
            request.flush();
            request.close();            
            String line = "";               
            InputStreamReader isr = new InputStreamReader(connection.getInputStream());
            BufferedReader reader = new BufferedReader(isr);
            StringBuilder sb = new StringBuilder();
            while ((line = reader.readLine()) != null)
            {
                sb.append(line + "\n");
            }
            // Response from server after login process will be stored in response variable.                
            response = sb.toString();
            // You can perform UI operations here
            isr.close();
            reader.close();
        }
        catch(IOException e)
        {
            // Error
        }
        System.out.println(response);
        JSONObject jObj = null;        
        // Try to parse the string to a JSON Object
        try {
            jObj = new JSONObject(response);
        } catch (JSONException e) {
            Log.e("JSON Parser", "Error parsing data " + e.toString());
        }

        // Return the JSONObject
        return jObj;
    }

    protected void onPostExecute(JSONObject json) {
        String status = (String) json.optJSONArray("users").optJSONObject(0).optJSONObject("user").optString("login");
        pDialog.dismiss();
        if (status.equals("Failed")) {
            loginMessage.setText("Login Failed");   
        }
        else if (status.equals("Success")) {
            loginMessage.setText("Success!");   
            startActivity(new Intent(getApplicationContext(), DActivity.class));
        }

    }
}

现在,我正在尝试使用Retrofit获得相同的结果,但是我不确定如何使用回调从我们的API获取JSON(我假设此调用应该异步发生?):

我已经创建了一个带有以下方法的接口:

    @FormUrlEncoded
@POST("/login")
public void login(@Field("username") String username, @Field("password") String password, Callback<JSONObject> callback);

我在Activity的onCreate方法中实例化了RestAdapter等内容。当用户按下“登录”按钮(在输入用户名和密码后),以下内容将在按钮的onClick方法中调用:

    service.login(email, password, new Callback<JSONObject>() { 
            @Override
            public void failure(final RetrofitError error) {
                android.util.Log.i("example", "Error, body: " + error.getBody().toString());
            }
            @Override
            public void success(JSONObject arg0, Response arg1) {

            }
        }
        );

但是,这并没有按照预期工作,我真的不认为我的方法正确。我想能够向我们的服务器发出POST请求,并返回关于该用户的JSON格式数据块。如果有人能指点我正确的方向,我将不胜感激。提前致谢。


1
你正在使用哪个Android API? - DJhon
@BlueGreen 我正在使用我们公司自己的API,所以我尝试使用Retrofit来进行GET/POST调用。 - mike
1
我现在正在使用Retrofit测试我的REST,做着和你一样的事情,但我还没有准备好给你一个完整的答案。 首先尝试使用Callback<Response> - 这应该会给你一个原始数据。 总体上看,你的代码似乎是正确的。 - AAverin
我目前也遇到了同样的问题。你成功解决了吗? - Smalesy
你成功了吗?我也在做同样的事情,但是我尝试使用回调函数进行异步POST并且使用FormUrlEncoded时出现了IllegalArgumentException异常 - https://dev59.com/SGEh5IYBdhLWcg3wTB72 - Rickster
2个回答

22

Retrofit的好处之一是无需自己解析JSON。你应该拥有类似于以下内容:

service.login(email, password, new Callback<User>() { 
        @Override
        public void failure(final RetrofitError error) {
            android.util.Log.i("example", "Error, body: " + error.getBody().toString());
        }
        @Override
        public void success(User user, Response response) {
            // Do something with the User object returned
        }
    }
);

其中 User 是一个类似于 POJO 的对象

public class User {
    private String name;
    private String email;
    // ... etc.
}

并且返回的JSON具有与User类匹配的字段:

{
  "name": "Bob User",
  "email": "bob@example.com",
  ...
}

如果您需要自定义解析,则应在设置REST适配器的转换器时使用.setConverter(Converter converter)

用于序列化和反序列化对象的转换器。


优秀的示例,所有方程的“部分”都清晰地展示出来了。不错。 - Joshua Pinter
2
如果我们得到的是 JSON 数组 '[' 而不是对象 '{',那怎么办? [ "name": "Bob User", "email": "bob@example.com", ... ] - LOG_TAG
对于数组,您可以使用List<User>。 - BratAnon
6
发送回去,那不是JSON格式。 - blahdiblah

8

似乎Retrofit在使用@POST@FormUrlEncoded时出现了问题。如果我们同步执行,它可以正常工作,但异步执行则失败。

如果@mike的问题是反序列化对象,那么User类应该是:

public class User {
     @SerializedName("name")
     String name;

     @SerializedName("email")
     String email;
}

接口类

@FormUrlEncoded
@POST("/login")
void login(@Field("username") String username, @Field("password") String password, Callback<User> callback);

或者

@FormUrlEncoded
@POST("/login")
void login(@Field("username") String username, @Field("password") String password, Callback<UserResponse> callback);

Where

public class UserResponse {

@SerializedName("email")
String email;

@SerializedName("name")
String name;

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接