这个问题以前可能被问过,但没有明确的答案。如何在Retrofit请求的主体中发布原始的整个JSON ?

在这里看到类似的问题。或者这个答案是正确的,它必须是表单url编码并作为一个字段传递?我真的希望不是,因为我要连接的服务只是希望在文章正文中有原始JSON。它们不是用来为JSON数据寻找特定字段的。

I just want to clarify this with the restperts once and for all. One person answered not to use Retrofit. The other was not certain of the syntax. Another thinks yes it can be done but only if its form url-encoded and placed in a field (that's not acceptable in my case). No, I can't re-code all the services for my Android client. And yes, it's very common in major projects to post raw JSON instead of passing over JSON content as field property values. Let's get it right and move on. Can someone point to the documentation or example that shows how this is done? Or provide a valid reason why it can/should not be done.

更新:有一件事我可以百分之百确定。你可以在谷歌的Volley中做到这一点。这是与生俱来的。我们可以在Retrofit中这样做吗?


当前回答

1)——添加依赖关系

 compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

2)创建Api Handler类

    public class ApiHandler {


  public static final String BASE_URL = "URL";  

    private static Webservices apiService;

    public static Webservices getApiService() {

        if (apiService == null) {

           Gson gson = new GsonBuilder()
                    .setLenient()
                    .create();
            Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(gson)).baseUrl(BASE_URL).build();

            apiService = retrofit.create(Webservices.class);
            return apiService;
        } else {
            return apiService;
        }
    }


}

3)从Json模式2 pojo制作bean类

还记得 -目标语言:Java -源类型:JSON 注释风格:Gson -select包含getter和setter -你也可以选择允许附加属性

http://www.jsonschema2pojo.org/

4)建立API调用接口

    public interface Webservices {

@POST("ApiUrlpath")
    Call<ResponseBean> ApiName(@Body JsonObject jsonBody);

}

如果你有一个表单数据参数,那么在下面添加

@Headers("Content-Type: application/x-www-form-urlencoded")

其他方式的表单数据参数检查此链接

5)make JsonObject作为参数传递给body

 private JsonObject ApiJsonMap() {

    JsonObject gsonObject = new JsonObject();
    try {
        JSONObject jsonObj_ = new JSONObject();
        jsonObj_.put("key", "value");
        jsonObj_.put("key", "value");
        jsonObj_.put("key", "value");


        JsonParser jsonParser = new JsonParser();
        gsonObject = (JsonObject) jsonParser.parse(jsonObj_.toString());

        //print parameter
        Log.e("MY gson.JSON:  ", "AS PARAMETER  " + gsonObject);

    } catch (JSONException e) {
        e.printStackTrace();
    }

    return gsonObject;
}

6)像这样调用Api

private void ApiCallMethod() {
    try {
        if (CommonUtils.isConnectingToInternet(MyActivity.this)) {
            final ProgressDialog dialog;
            dialog = new ProgressDialog(MyActivity.this);
            dialog.setMessage("Loading...");
            dialog.setCanceledOnTouchOutside(false);
            dialog.show();

            Call<ResponseBean> registerCall = ApiHandler.getApiService().ApiName(ApiJsonMap());
            registerCall.enqueue(new retrofit2.Callback<ResponseBean>() {
                @Override
                public void onResponse(Call<ResponseBean> registerCall, retrofit2.Response<ResponseBean> response) {

                    try {
                        //print respone
                        Log.e(" Full json gson => ", new Gson().toJson(response));
                        JSONObject jsonObj = new JSONObject(new Gson().toJson(response).toString());
                        Log.e(" responce => ", jsonObj.getJSONObject("body").toString());

                        if (response.isSuccessful()) {

                            dialog.dismiss();
                            int success = response.body().getSuccess();
                            if (success == 1) {



                            } else if (success == 0) {



                            }  
                        } else {
                            dialog.dismiss();


                        }


                    } catch (Exception e) {
                        e.printStackTrace();
                        try {
                            Log.e("Tag", "error=" + e.toString());

                            dialog.dismiss();
                        } catch (Resources.NotFoundException e1) {
                            e1.printStackTrace();
                        }

                    }
                }

                @Override
                public void onFailure(Call<ResponseBean> call, Throwable t) {
                    try {
                        Log.e("Tag", "error" + t.toString());

                        dialog.dismiss();
                    } catch (Resources.NotFoundException e) {
                        e.printStackTrace();
                    }
                }

            });

        } else {
            Log.e("Tag", "error= Alert no internet");


        }
    } catch (Resources.NotFoundException e) {
        e.printStackTrace();
    }
}

其他回答

@Body注释定义了一个请求主体。

interface Foo {
  @POST("/jayson")
  FooResponse postJson(@Body FooRequest body);
}

由于Retrofit默认使用Gson,因此FooRequest实例将被序列化为JSON作为请求的唯一主体。

public class FooRequest {
  final String foo;
  final String bar;

  FooRequest(String foo, String bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

调用:

FooResponse = foo.postJson(new FooRequest("kit", "kat"));

将产生以下主体:

{"foo":"kit","bar":"kat"}

Gson文档中有更多关于对象序列化如何工作的内容。

现在,如果你真的非常想自己发送“原始”JSON作为正文(但请使用Gson !),你仍然可以使用TypedInput:

interface Foo {
  @POST("/jayson")
  FooResponse postRawJson(@Body TypedInput body);
}

TypedInput被定义为“具有关联mime类型的二进制数据”。有两种方法可以使用上述声明轻松发送原始数据:

Use TypedByteArray to send raw bytes and the JSON mime type: String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}"; TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8")); FooResponse response = foo.postRawJson(in); Subclass TypedString to create a TypedJsonString class: public class TypedJsonString extends TypedString { public TypedJsonString(String body) { super(body); } @Override public String mimeType() { return "application/json"; } } And then use an instance of that class similar to #1.

我们也可以直接使用HashMap<String, Object>来发送主体参数,而不是类 例如

interface Foo {
  @POST("/jayson")
  FooResponse postJson(@Body HashMap<String, Object> body);
}

你需要在interface中设置@Body

@Headers({ "Content-Type: application/json;charset=UTF-8"})
    @POST("Auth/Login")
    Call<ApiResponse> loginWithPhone(@Body HashMap<String, String> fields);

要通过生身改装就用:

 HashMap<String,String> SendData =new HashMap<>();
        SendData.put("countryCode",ccode);
        SendData.put("phoneNumber",phone);

        Call<ApiResponse>call = serviceInterface.loginWithPhone(SendData);

这对我来说很管用:

我特别喜欢Jake对上面TypedString子类的建议。实际上,您可以基于计划推送的POST数据类型创建各种子类,每个子类都有自己的自定义一致调整集。

您还可以选择在您的Retrofit API中为您的JSON POST方法添加头注释…

@Headers( "Content-Type: application/json" )
@POST("/json/foo/bar/")
Response fubar( @Body TypedString sJsonBody ) ;

但是使用子类显然是自文档化的。

@POST("/json/foo/bar")
Response fubar( @Body TypedJsonString jsonBody ) ;

是的,我知道很晚了,但有人可能会从中受益。

使用Retrofit2:

我昨晚从Volley迁移到Retrofit2时遇到了这个问题(正如OP所述,这是用JsonObjectRequest构建到Volley中的),尽管Jake的答案是Retrofit1.9的正确答案,但Retrofit2没有TypedString。

我的情况下需要发送一个地图<字符串,对象>,可以包含一些空值,转换为JSONObject(不会飞@FieldMap,也不特殊字符,一些得到转换),所以遵循@ b规范提示,并由Square声明:

可以使用@Body注释指定对象作为HTTP请求主体。 对象还将使用Retrofit实例上指定的转换器进行转换。如果没有添加转换器,则只能使用RequestBody。

这是一个使用RequestBody和ResponseBody的选项:

在你的接口中使用@Body和RequestBody

public interface ServiceApi
{
    @POST("prefix/user/{login}")
    Call<ResponseBody> login(@Path("login") String postfix, @Body RequestBody params);  
}

在你的调用点创建一个RequestBody,声明它是MediaType,并使用JSONObject将你的Map转换为正确的格式:

Map<String, Object> jsonParams = new ArrayMap<>();
//put something inside the map, could be null
jsonParams.put("code", some_code);

RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(new JSONObject(jsonParams)).toString());
//serviceCaller is the interface initialized with retrofit.create...
Call<ResponseBody> response = serviceCaller.login("loginpostfix", body);
      
response.enqueue(new Callback<ResponseBody>()
    {
        @Override
        public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
        {
            try
            {
             //get your response....
              Log.d(TAG, "RetroFit2.0 :RetroGetLogin: " + rawResponse.body().string());
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable throwable)
        {
        // other stuff...
        }
    });

上面的一个优雅的Kotlin版本,允许在应用程序的其余代码中从JSON转换中抽象参数:

interface ServiceApi {

    @POST("/api/login")
    fun jsonLogin(@Body params: RequestBody): Deferred<LoginResult>

}

class ServiceApiUsingClass {

//ServiceApi init

    fun login(username: String, password: String) =
            serviceApi.jsonLogin(createJsonRequestBody(
                "username" to username, "password" to password))

    private fun createJsonRequestBody(vararg params: Pair<String, String>) =
            RequestBody.create(
                okhttp3.MediaType.parse("application/json; charset=utf-8"), 
                JSONObject(mapOf(*params)).toString())
}