首页 文章

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

提问于
浏览
245

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

看到类似的问题这里。或者这个答案是否正确必须是表单 url 编码并作为字段传递?我真的希望不会,因为我所连接的服务只是期待帖子正文中的原始 JSON。它们未设置为查找 JSON 数据的特定字段。

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

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

16 回答

  • 420

    @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 类型的二进制数据”。使用上述声明可以通过两种方式轻松发送原始数据:

    String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}";
    TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
    FooResponse response = foo.postRawJson(in);
    
    public class TypedJsonString extends TypedString {
      public TypedJsonString(String body) {
        super(body);
      }
    
      @Override public String mimeType() {
        return "application/json";
      }
    }
    

    然后使用类似#1 的那个类的实例。

  • 139

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

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

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

    使用 Retrofit2:

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

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

    可以指定一个对象用作带有 the_1_annotation 的 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())
    
    }
    
  • 68

    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", "sample@gmail.com");
                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是这样的:

    • 像这样创建你的界面:
    public interface laInterfaz{ 
        @POST("/bleh/blah/org")
        void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback);
    }
    
    • 使 JsonObject 符合 jsons 结构。
    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*/
    
    • 致电服务:
    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());
        }
    };
    

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

  • 10

    我特别喜欢杰克对TypedString子类以上的建议。您确实可以根据您计划推送的各种 POST 数据创建各种子类,每个子类都有自己的一组自定义调整。

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

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

    ...但是使用子类更明显是 self-documenting。

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

    1)Add 依赖 -

    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'
    
    1. 制作 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;
            }
        }
    
    }
    

    来自 Json schema 2 pojo 的 3)make bean 类

    记得
    -Target 语言:Java -Source 类型:JSON -Annotation 样式:Gson -select 包括 getter 和 setter-also 你可以选择允许其他属性

    http://www.jsonschema2pojo.org/

    来自 api 呼叫的 4)make 接口

    public interface Webservices {
    
    @POST("ApiUrlpath")
        Call<ResponseBean> ApiName(@Body JsonObject jsonBody);
    
    }
    

    如果你有一个 form-data 参数,那么添加下面的行

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

    form-data 参数检查的其他方法链接

    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;
    }
    
    1. 致电 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();
        }
    }
    
  • 6

    我发现当你使用复合对象作为@Body params 时,它对于 Retrofit 的GSONConverter(假设你正在使用它)不能很好地工作。使用JsonObject时,你必须使用JsonObject而不是JSONObject,它添加NameValueParams而不是冗长 - 你只能看到如果你添加另一个日志拦截器和其他恶作剧的依赖项。

    所以我发现解决这个问题的最佳方法是使用RequestBody。您可以通过简单的 api 调用将对象转换为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())
    

    这就是电话:

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

    添加 ScalarsConverterFactory 进行改造:

    在 gradle 中:

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

    你的改造:

    retrofit = new Retrofit.Builder()
                .baseUrl(WEB_DOMAIN_MAIN)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
    

    将您的调用 interface @Body parameter 更改为 String,不要忘记添加@Headers("Content-Type: application/json")

    @Headers("Content-Type: application/json")
    @POST("/api/getUsers")
    Call<List<Users>> getUsers(@Body String rawJsonString);
    

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

  • 4

    经过这么多的努力,发现基本的区别是你需要发送JsonObject而不是JSONObject作为参数。

  • 3

    使用以下来发送 json

    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());
    

    并将其传递给网址

    @Body RequestBody key
    
  • 3

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

    例如,我想发布这个 JSON。

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

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

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

    最后,当我需要一个 json

    WRequest request = new WRequest();
    request.data.put("type", type);
    request.data.put("page", page);
    

    请求标记为注释@Body然后可以传递给 Retrofit。

  • 2

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

    改造界面:

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

    呼叫:

    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

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

    gsonBuilder = new GsonBuilder().serializeNulls()     
    your_retrofit_instance = Retrofit.Builder().addConverterFactory( GsonConverterFactory.create( gsonBuilder.create() ) )
    
  • 1

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

    如果您使用的是com.squareup.okhttp3:okhttp:4.0.1,那么创建MediaTypeRequestBody对象的旧方法已被弃用,不能在 Kotlin 中使用。

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

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

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

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

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

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

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

    HashMap<String,String> hashMap=new HashMap<>();
            hashMap.put("email","this@gmail.com");
            hashMap.put("password","1234");
    

    然后像这样发送

    Call<JsonElement> register(@Body HashMap registerApiPayload);
    

相关问题