如何在Retrofit请求的主体中发布原始整个JSON?

问题描述 投票:236回答:15

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

看到类似的问题here。或者这个答案是正确的,它must be form url encoded and passed as a field?我真的希望不会,因为我所连接的服务只是期待帖子正文中的原始JSON。它们未设置为查找JSON数据的特定字段。

我只想一劳永逸地与其他人一起澄清这一点。一个人回答不使用Retrofit。另一个不确定语法。另一个人认为可以这样做,但只有当它的形式url编码并放在一个字段中时(在我的情况下这是不可接受的)。不,我无法为我的Android客户端重新编码所有服务。是的,在主要项目中发布原始JSON而不是将JSON内容作为字段属性值传递是很常见的。让我们做对了,继续前进吧。有人可以指向显示如何完成此操作的文档或示例吗?或者提供可以/不应该完成的有效理由。

更新:我可以100%确定地说一件事。你可以在谷歌的排球中做到这一点。它是内置的。我们可以在Retrofit中做到这一点吗?

android rest http-post retrofit android-json
15个回答
425
投票

@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 docs对于对象序列化的工作原理有很多。

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

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

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

  1. 使用TypedByteArray发送原始字节和JSON mime类型: String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}"; TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8")); FooResponse response = foo.postRawJson(in);
  2. 子类TypedString创建一个TypedJsonString类: public class TypedJsonString extends TypedString { public TypedJsonString(String body) { super(body); } @Override public String mimeType() { return "application/json"; } } 然后使用类似#1的那个类的实例。

4
投票

经过这么多的努力,发现基本的区别是你需要发送@Headers("Content-Type: application/json") @POST("/api/getUsers") Call<List<Users>> getUsers(@Body String rawJsonString); 而不是JsonObject作为参数。


3
投票

使用以下来发送json

JSONObject

并将其传递给网址

final JSONObject jsonBody = new JSONObject();
    try {

        jsonBody.put("key", "value");

    } catch (JSONException e){
        e.printStackTrace();
    }
    RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(jsonBody).toString());

3
投票

根据最佳答案,我有一个解决方案,不必为每个请求制作POJO。

例如,我想发布这个JSON。

@Body RequestBody key

然后,我创建一个这样的公共类:

{
    "data" : {
        "mobile" : "qwer",
        "password" : "qwer"
    },
    "commom" : {}
}

最后,当我需要一个json

import java.util.Map;
import java.util.HashMap;

public class WRequest {

    Map<String, Object> data;
    Map<String, Object> common;

    public WRequest() {
        data = new HashMap<>();
        common = new HashMap<>();
    }
}

请求标记注释WRequest request = new WRequest(); request.data.put("type", type); request.data.put("page", page); 然后可以传递给Retrofit。


2
投票

如果您不想创建额外的类或使用@Body,您可以使用JSONObject

改造界面:

HashMap

呼叫:

@POST("/rest/registration/register")
fun signUp(@Body params: HashMap<String, String>): Call<ResponseBody>

1
投票

我试过这个:在创建Retrofit实例时,将此转换器工厂添加到改造构建器:

val map = hashMapOf(
    "username" to username,
    "password" to password,
    "firstName" to firstName,
    "surname" to lastName
)

retrofit.create(TheApi::class.java)
     .signUp(map)
     .enqueue(callback)

1
投票

为了更清楚地说明这里给出的答案,您可以使用扩展功能。这只有在您使用Kotlin的情况下

如果您使用gsonBuilder = new GsonBuilder().serializeNulls() your_retrofit_instance = Retrofit.Builder().addConverterFactory( GsonConverterFactory.create( gsonBuilder.create() ) ) ,则不推荐使用创建MediaType和RequestBody对象的旧方法,并且不能在Kotlin中使用。

如果要使用扩展函数从字符串中获取MediaType对象和ResponseBody对象,请首先将以下行添加到您希望使用它们的类中。

com.squareup.okhttp3:okhttp:4.0.1

您现在可以通过这种方式直接获取MediaType的对象

import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

要获取RequestBody的对象,首先将要发送的JSONObject转换为字符串。您必须将mediaType对象传递给它。

val mediaType = "application/json; charset=utf-8".toMediaType()

0
投票

如果您不想为每个API调用创建pojo类,则可以使用hashmap。

val requestBody = myJSONObject.toString().toRequestBody(mediaType)

然后像这样发送

HashMap<String,String> hashMap=new HashMap<>();
        hashMap.put("email","[email protected]");
        hashMap.put("password","1234");

139
投票

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

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

134
投票

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

使用Retrofit2:

我昨晚遇到了这个问题,从Volley迁移到Retrofit2(并且作为OP状态,这是用JsonObjectRequest直接进入Volley),虽然Jake的答案是Retrofit1.9的正确答案,但Retrofit2没有TypedString

我的情况需要发送一个可能包含一些空值的Map<String,Object>,转换为JSONObject(不会与@FieldMap一起飞行,特殊字符也不会转换,有些转换),所以关注@bnorms提示,并且如Square所述:

可以指定一个对象用作带有@Body注释的HTTP请求主体。

该对象也将使用Retrofit实例上指定的转换器进行转换。如果未添加转换器,则只能使用RequestBody。

所以这是使用RequestBodyResponseBody的选项:

在你的界面中使用@BodyRequestBody

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 {

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

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

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

}

71
投票

在Retrofit2中,如果要以raw格式发送参数,则必须使用Scalars。

首先在你的gradle中添加:

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'

你的界面

public interface ApiInterface {

    String URL_BASE = "http://10.157.102.22/rest/";

    @Headers("Content-Type: application/json")
    @POST("login")
    Call<User> getUser(@Body String body);

}

活动

   public class SampleActivity extends AppCompatActivity implements Callback<User> {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ApiInterface.URL_BASE)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        ApiInterface apiInterface = retrofit.create(ApiInterface.class);


        // prepare call in Retrofit 2.0
        try {
            JSONObject paramObject = new JSONObject();
            paramObject.put("email", "[email protected]");
            paramObject.put("pass", "4384984938943");

            Call<User> userCall = apiInterface.getUser(paramObject.toString());
            userCall.enqueue(this);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void onResponse(Call<User> call, Response<User> response) {
    }

    @Override
    public void onFailure(Call<User> call, Throwable t) {
    }
}

39
投票

使用JsonObject是这样的:

  1. 像这样创建你的界面: public interface laInterfaz{ @POST("/bleh/blah/org") void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback); }
  2. 根据jsons结构制作Json对象。 qazxsw poi
  3. 致电服务: JsonObject obj = new JsonObject(); JsonObject payerReg = new JsonObject(); payerReg.addProperty("crc","aas22"); payerReg.addProperty("payerDevManufacturer","Samsung"); obj.add("payerReg",payerReg); /*json/* {"payerReg":{"crc":"aas22","payerDevManufacturer":"Samsung"}} /*json*/

那就是它!在我个人看来,它比制作pojos和处理课堂混乱要好得多。这更清洁了。


10
投票

我特别喜欢杰克对service.registerPayer(obj, callBackRegistraPagador); Callback<JsonObject> callBackRegistraPagador = new Callback<JsonObject>(){ public void success(JsonObject object, Response response){ System.out.println(object.toString()); } public void failure(RetrofitError retrofitError){ System.out.println(retrofitError.toString()); } }; 子类TypedString的建议。您确实可以根据您计划推送的各种POST数据创建各种子类,每个子类都有自己的一组自定义调整。

您还可以选择在Re​​trofit API中的JSON POST方法中添加标题注释...

above

...但是使用子类更明显是自我记录。

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

9
投票

1)添加依赖项 -

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

2)制作Api Handler类

 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'

3)从Json schema 2 pojo制作bean类

记得 -Target语言:Java -Source类型:JSON -Annotation样式:Gson -select包含getter和setter - 也可以选择Allow additional properties

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

4)为api调用创建接口

http://www.jsonschema2pojo.org/

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

    public interface Webservices {

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

}

表格数据参数的其他方式检查这个@Headers("Content-Type: application/x-www-form-urlencoded")

5)使JsonObject作为参数传入body

link

6)致电Api像这样

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

7
投票

我发现当你使用复合物体作为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(); } } params时,它无法与Retrofit的@Body(假设你正在使用它)一起使用。你必须使用GSONConverter,而不是使用JsonObject,它添加JSONObject而不是冗长 - 你只能看到如果你添加另一个日志拦截器和其他恶作剧的依赖。

所以我发现解决这个问题的最佳方法是使用NameValueParams。您可以通过简单的api调用将对象转换为RequestBody并启动它。在我的情况下,我正在转换地图:

RequestBody

这就是电话:

   val map = HashMap<String, Any>()
        map["orderType"] = orderType
        map["optionType"] = optionType
        map["baseAmount"] = baseAmount.toString()
        map["openSpotRate"] = openSpotRate.toString()
        map["premiumAmount"] = premiumAmount.toString()
        map["premiumAmountAbc"] = premiumAmountAbc.toString()
        map["conversionSpotRate"] = (premiumAmountAbc / premiumAmount).toString()
        return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSONObject(map).toString())

6
投票

添加ScalarsConverterFactory进行改造:

在gradle中:

 @POST("openUsvDeal")
fun openUsvDeal(
        @Body params: RequestBody,
        @Query("timestamp") timeStamp: Long,
        @Query("appid") appid: String = Constants.APP_ID,
): Call<JsonObject>

你的改造:

implementation'com.squareup.retrofit2:converter-scalars:2.5.0'

将您的调用接口@Body参数更改为String,不要忘记添加retrofit = new Retrofit.Builder() .baseUrl(WEB_DOMAIN_MAIN) .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson)) .build();

@Headers("Content-Type: application/json")

现在你可以发布原始的json。

© www.soinside.com 2019 - 2024. All rights reserved.