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

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

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

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

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

Ответ 1

Аннотация @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.

Ответ 2

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

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

Ответ 3

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

Использование 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())

}

Ответ 4

В 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) {
    }
}

Ответ 5

Использование 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());
        }
    };
    

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

Ответ 6

Мне особенно нравится предложение Джейка подкласса 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 ) ;

Ответ 7

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

Ответ 8

Я обнаружил, что когда вы используете составной объект в @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>

Ответ 9

Добавьте 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.

Ответ 10

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

Ответ 11

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

Ответ 12

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

Ответ 13

Если вы не хотите создавать дополнительные классы или использовать 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)

Ответ 14

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

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

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

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

Ответ 15

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

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

Ответ 16

Для большей ясности в ответах, приведенных здесь, вы можете использовать функции расширения. Это только если вы используете Kotlin

Если вы используете com.squareup.okhttp3:okhttp:4.0.1, старые методы создания объектов MediaType и RequestBody устарели и не могут использоваться в 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)

Ответ 17

Решил мою проблему, основываясь на ответе TommySM (см. предыдущий). Но мне не нужно было входить в систему, я использовал Retrofit2 для тестирования https GraphQL API следующим образом:

  1. Определил мой класс BaseResponse с помощью аннотаций json (импорт jackson.annotation.JsonProperty).

    public class MyRequest {
        @JsonProperty("query")
        private String query;
    
        @JsonProperty("operationName")
        private String operationName;
    
        @JsonProperty("variables")
        private String variables;
    
        public void setQuery(String query) {
            this.query = query;
        }
    
        public void setOperationName(String operationName) {
            this.operationName = operationName;
        }
    
        public void setVariables(String variables) {
            this.variables = variables;
        }
    }
    
  2. Определена процедура вызова в интерфейсе:

    @POST("/api/apiname")
    Call<BaseResponse> apicall(@Body RequestBody params);
    
  3. Вызывается apicall в теле теста: Создайте переменную типа MyRequest (например, "myLittleRequest").

    Map<String, Object> jsonParams = convertObjectToMap(myLittleRequest);
    RequestBody body = 
         RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
                        (new JSONObject(jsonParams)).toString());
    response = hereIsYourInterfaceName().apicall(body).execute();