Как выполнить POST-сырое целое JSON в теле запроса на доработку?

Этот вопрос, возможно, был задан раньше, но он не был окончательно ответил. Как точно один почтовый цельный JSON внутри тела запроса Retrofit?

См. аналогичный вопрос здесь. Или этот ответ правильный, что он должен быть закодирован в url и передан как поле? Я действительно надеюсь, что нет, поскольку службы, с которыми я связываюсь, просто ждут сырой JSON в теле сообщения. Они не настроены для поиска определенного поля для данных JSON.

Я просто хочу прояснить это с помощью restpts раз и навсегда. Один человек ответил, что не использует "Дооснащение". Другой не был уверен в синтаксисе. Другой думает, да, это можно сделать, но только если его форма была закодирована и помещена в поле (что неприемлемо в моем случае). Нет, я не могу перекодировать все службы для моего Android-клиента. И да, это очень распространено в крупных проектах для публикации raw JSON вместо передачи содержимого JSON в качестве значений свойства поля. Позвольте понять это правильно и двигаться дальше. Может ли кто-нибудь указать на документацию или пример, показывающий, как это делается? Или укажите действительную причину, по которой это может/не должно быть сделано.

ОБНОВЛЕНИЕ: Я могу сказать со 100% уверенностью. Вы можете сделать это в Google Volley. Он построен прямо. Можем ли мы сделать это в "Дооснащении"?

+232
28 янв. '14 в 6:40
источник поделиться
15 ответов

Аннотация @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". Есть два способа легко отправить необработанные данные с указанным выше объявлением:

  • Используйте TypedByteArray для отправки необработанных байтов и типа Mime JSON:

    String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}";
    TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
    FooResponse response = foo.postRawJson(in);
    
  • Подкласс TypedString для создания класса TypedJsonString:

    public class TypedJsonString extends TypedString {
      public TypedJsonString(String body) {
        super(body);
      }
    
      @Override public String mimeType() {
        return "application/json";
      }
    }
    

    И затем используйте экземпляр этого класса, аналогичный # 1.

+412
29 янв. '14 в 5:47
источник

Связанные вопросы


Похожие вопросы

Вместо классов мы также можем напрямую использовать HashMap<String, Object> для отправки параметров тела например

interface Foo {
  @POST("/jayson")
  FooResponse postJson(@Body HashMap<String, Object> body);
}
+135
25 февр. '15 в 6:04
источник

Да, я знаю это поздно, но кто-то, вероятно, выиграет от этого.

Использование Retrofit2:

Вчера вечером я столкнулся с этой проблемой при переходе с Volley на Retrofit2 (и, как утверждает OP, он был встроен прямо в Volley с JsonObjectRequest), и хотя ответ Джейка является правильным для Retrofit1.9, Retrofit2 не имеет TypedString.

В моем случае требовалось отправить Map<String,Object> которая могла бы содержать некоторые нулевые значения, преобразованные в JSONObject (который не будет летать с @FieldMap, также как и специальные символы, некоторые преобразованы), так что следуйте подсказке @bnorms и как заявлено по площади:

Объект может быть указан для использования в качестве тела HTTP-запроса с аннотацией @Body.

Объект также будет преобразован с использованием преобразователя, указанного в экземпляре 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<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())

}
+128
24 апр. '16 в 9:07
источник

В 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) {
    }
}
+63
22 мая '17 в 1:54
источник

Использование JsonObject:

  1. Создайте свой интерфейс так:

    public interface laInterfaz{ 
        @POST("/bleh/blah/org")
        void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback);
    }
    
  2. Сделайте 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*/
    
  3. Позвоните в сервис:

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

И это его! По моему личному мнению, это намного лучше, чем делать поно и работать с классом. Это намного чище.

+36
27 июл. '15 в 16:08
источник

Мне особенно нравится предложение Джейка подкласса TypedString выше. Вы действительно можете создать множество подклассов, основанных на тех типах данных POST, которые вы планируете выдвигать, каждый со своим собственным набором согласованных настроек.

У вас также есть возможность добавить аннотацию заголовка к вашим методам POST JSON в вашем API-интерфейсе...

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

... но использование подкласса более очевидно самодокументируется.

@POST("/json/foo/bar")
Response fubar( @Body TypedJsonString jsonBody ) ;
+10
11 мар. '15 в 13:48
источник

1) Добавить dependencies-

 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

Помните
-Target язык: Java -Source тип: JSON -Annotation стиль: Gson -select Включать геттеры и сеттеры -also, которые вы можете выбрать Разрешить дополнительные свойства

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) сделать JsonObject для передачи в тело в качестве параметра

 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();
    }
}
+7
17 янв. '18 в 11:47
источник

После стольких усилий выяснилось, что основное отличие заключается в том, что вам нужно отправлять JsonObject вместо JSONObject качестве параметра.

+4
05 мар. '18 в 18:03
источник

Добавьте ScalarsConverterFactory для модернизации:

в градле:

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

измените параметр @Body интерфейса вызова на String, не забудьте добавить @Headers("Content-Type: application/json"):

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

Теперь вы можете опубликовать сырой JSON.

+4
24 янв. '19 в 5:18
источник

Основываясь на верхнем ответе, у меня есть решение не делать 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.

+3
10 авг. '18 в 13:01
источник

Я обнаружил, что когда вы используете составной объект в @Body параметра @Body, он не может хорошо работать с Retrofit GSONConverter (при условии, что вы используете это). Вы должны использовать JsonObject а не JSONObject при работе с ним, он добавляет NameValueParams не NameValueParams подробности об этом - вы можете увидеть это, только если добавите еще одну зависимость от перехватчика регистрации и других махинаций.

Так что я нашел лучший подход для решения этой проблемы - это использование RequestBody. Вы превращаете свой объект в RequestBody с помощью простого вызова API и запускаете его. В моем случае я конвертирую карту:

   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>
+3
28 янв. '19 в 8:47
источник

используйте следующее, чтобы отправить 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());

и передать его URL

@Body RequestBody key
+3
02 авг. '18 в 19:27
источник

Если вы не хотите создавать дополнительные классы или использовать 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)
+2
22 янв. '19 в 20:02
источник

Я попробовал это: Когда вы создаете свой экземпляр Retrofit, добавьте эту фабрику конвертеров в построитель Retrofit:

gsonBuilder = new GsonBuilder().serializeNulls()     
your_retrofit_instance = Retrofit.Builder().addConverterFactory( GsonConverterFactory.create( gsonBuilder.create() ) )
+1
18 окт. '18 в 13:32
источник

Вы можете использовать hashmap, если не хотите создавать класс pojo для каждого вызова API.

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

А потом отправь вот так

Call<JsonElement> register(@Body HashMap registerApiPayload);
0
05 мар. '19 в 11:03
источник

Посмотрите другие вопросы по меткам или Задайте вопрос