web-dev-qa-db-fra.com

Comment POST JSON entier brut dans le corps d'une demande de modification?

Cette question a peut-être déjà été posée, mais non, il n'a pas été répondu définitivement. Comment exactement post-on un JSON entier brut dans le corps d'une demande de modification?

Voir la question similaire ici . Ou bien cette réponse indique-t-elle qu'elle doit être une URL de formulaire codée et passée sous forme de champ ? J'espère vraiment que non, car les services auxquels je me connecte n'attendent que du JSON brut dans le corps du message. Ils ne sont pas configurés pour rechercher un champ particulier pour les données JSON.

Je veux juste clarifier cela avec les restperts une fois pour toutes. Une personne a répondu ne pas utiliser Retrofit. L'autre n'était pas certain de la syntaxe. Un autre pense que oui, cela peut être fait, mais seulement si sa forme est encodée dans une URL et placée dans un champ (ce qui n'est pas acceptable dans mon cas). Non, je ne peux pas recoder tous les services de mon client Android. Et oui, il est très courant dans les grands projets de publier du JSON brut au lieu de transmettre le contenu JSON en tant que valeurs de propriété de champ. Allons bien et passons à autre chose. Quelqu'un peut-il indiquer la documentation ou l'exemple qui montre comment cela est fait? Ou donnez une raison valable pour laquelle cela peut/ne devrait pas être fait.

MISE À JOUR: Une chose que je peux dire avec 100% de certitude. Vous pouvez le faire dans Google Volley. Il est intégré. Pouvons-nous le faire en rénovation?

215
user3243335

L'annotation @Body définit un corps de requête unique.

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

Dans la mesure où Retrofit utilise Gson par défaut, les instances FooRequest seront sérialisées en tant que JSON en tant que corps unique de la demande.

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

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

Appeler avec:

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

Donnera le corps suivant:

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

Les docs Gson ont beaucoup plus sur le fonctionnement de la sérialisation des objets.

Maintenant, si vous voulez vraiment envoyer du JSON "brut" en tant que corps (mais utilisez Gson pour cela!), Vous pouvez toujours utiliser TypedInput:

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

TypedInput est défini comme "Données binaires avec un type mime associé". Il existe deux manières d'envoyer facilement des données brutes avec la déclaration ci-dessus:

  1. Utilisez TypedByteArray pour envoyer des octets bruts et le type mime JSON:

    String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}";
    TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
    FooResponse response = foo.postRawJson(in);
    
  2. Sous-classe TypedString pour créer une classe TypedJsonString:

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

    Et utilisez ensuite une instance de cette classe similaire à # 1.

393
Jake Wharton

Au lieu de classes, nous pouvons également utiliser directement le HashMap<String, Object> pour envoyer des paramètres de corps Par exemple

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

Oui, je sais qu'il est tard, mais quelqu'un en profiterait probablement.

Utilisation de Retrofit2:

Je suis tombé sur ce problème la nuit dernière lors de la migration de Volley vers Retrofit2 (et comme le dit l'OP, il a été intégré à Volley avec JsonObjectRequest), et bien que la réponse de Jake soit la bonne pour Retrofit1.9, Retrofit2 ne l'a pas TypedString

Mon cas nécessitait l'envoi d'un Map<String,Object> pouvant contenir des valeurs NULL, converti en JSONObject (qui ne volera pas avec @FieldMap, aucun caractère spécial ne sera converti, certains seront convertis), donc, suivant l'attribut @bnorms et comme indiqué par Square :

Un objet peut être spécifié pour être utilisé en tant que corps de requête HTTP avec l'annotation @Body.

L'objet sera également converti à l'aide d'un convertisseur spécifié dans l'instance Retrofit. Si aucun convertisseur n'est ajouté, seul RequestBody peut être utilisé.

Il s’agit donc d’une option utilisant RequestBody et ResponseBody:

Dans votre interface, utilisez @Body avec RequestBody

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

Dans votre point d'appel, créez une RequestBody, indiquant son type de média et utilisant JSONObject pour convertir votre carte au format approprié:

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

J'espère que cela aide n'importe qui!


Une version Kotlin élégante de ce qui précède, pour permettre d’abstraire les paramètres de la conversion JSON dans le reste du code de votre application:

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

}
122
TommySM

Dans Retrofit2 , Lorsque vous voulez envoyer vos paramètres brut, vous devez utiliser Scalars.

ajoutez d'abord ceci dans votre grade:

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'

Votre interface

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

}

Activité

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

Utiliser JsonObject, c'est comme ça: 

  1. Créez votre interface comme ceci:

    public interface laInterfaz{ 
        @POST("/bleh/blah/org")
        void registerPayer(@Body JsonObject bean, Callback<JsonObject> callback);
    }
    
  2. Faites en sorte que JsonObject corresponde à la structure de 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. Appeler le service: 

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

Et que sa! À mon avis, c'est beaucoup mieux que de faire des pojos et de travailler avec le désordre de la classe. C'est beaucoup plus propre.

34
superUser

J'aime particulièrement la suggestion de Jake de la sous-classe TypedStringci-dessus . Vous pouvez en effet créer une variété de sous-classes basées sur les types de données POST que vous prévoyez de transférer, chacune avec son propre ensemble personnalisé d'ajustements cohérents.

Vous avez également la possibilité d'ajouter une annotation d'en-tête à vos méthodes JSON POST dans votre API de modification ...

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

… Mais l'utilisation d'une sous-classe est plus clairement auto-documentée.

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

1) Ajouter des dépendances-

 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) faire la classe 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) créer des classes de haricots à partir du schéma Json 2 pojo

Rappelles toi
Langage cible: Java -Type de source: JSON Style de style: Gson -select Include getters and setters -Vous pouvez aussi sélectionner Autoriser des propriétés supplémentaires

http://www.jsonschema2pojo.org/

4) faire l'interface de l'appel api

    public interface Webservices {

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

}

si vous avez des paramètres de données de formulaire, ajoutez la ligne ci-dessous

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

Autre moyen pour le paramètre de données de formulaire vérifier ceci link

5) faire JsonObject pour passer à corps comme paramètre

 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) Appeler Api comme ça

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

Après tant d'efforts, nous avons constaté que la différence fondamentale est que vous devez envoyer JsonObject au lieu de JSONObject en tant que paramètre.

4
umair151

Sur la base de la réponse principale, j'ai une solution pour ne pas avoir à créer de POJO pour chaque demande.

Exemple, je veux publier ce JSON.

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

alors, je crée une classe commune comme celle-ci:

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

Enfin, quand j’ai besoin d’un json

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

La requête marquée l'annotation @Body peut alors passer à Retrofit.

3
wjploop

utiliser suivant pour envoyer 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());

et le passer à l'URL

@Body RequestBody key
2
Mahesh Pandit

Si vous ne voulez pas créer de classes supplémentaires ou utiliser JSONObject, vous pouvez utiliser un HashMap.

Interface de modernisation:

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

Appel:

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
SoftDesigner

Ajoutez ScalarsConverterFactory à la conversion:

en grade:

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

votre retrofit:

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

remplacez le paramètre @Body de votre interface d'appel par String, n'oubliez pas d'ajouter @Headers("Content-Type: application/json"):

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

maintenant, vous pouvez poster des images brutes.

2
ali-star

J'ai trouvé que lorsque vous utilisez un objet composé en tant que @Body params, il ne fonctionnerait pas bien avec la variable GSONConverter de Retrofit (en supposant que vous l'utilisiez) . Vous devez utiliser JsonObject et non JSONObject lorsque vous travaillez avec cela, ajoute-t-il NameValueParams sans être clair - vous ne pouvez le voir que si vous ajoutez une autre dépendance de l'intercepteur de journalisation, et d'autres manigances.

Donc, ce que j’ai trouvé, la meilleure approche pour y remédier consiste à utiliser RequestBody. Vous transformez votre objet en RequestBody avec un simple appel api et le lancez . Dans mon cas, je suis en train de convertir une carte: 

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

et c'est l'appel:

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

Pour plus de clarté sur les réponses données ici, voici comment utiliser les fonctions d’extension. Ceci est seulement si vous utilisez Kotlin

Si vous utilisez com.squareup.okhttp3:okhttp:4.0.1, les anciens outils de création d'objets MediaType et RequestBody sont obsolètes et ne peuvent pas être utilisés dans . Kotlin .

Si vous souhaitez utiliser les fonctions d'extension pour obtenir un objet MediaType et un objet ResponseBody de vos chaînes, ajoutez tout d'abord les lignes suivantes à la classe dans laquelle vous comptez les utiliser. .

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

Vous pouvez maintenant obtenir directement un objet de MediaType de cette façon

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

Pour obtenir un objet de RequestBody, convertissez d’abord le JSONObject que vous voulez envoyer en chaîne de cette façon. Vous devez lui passer l'objet mediaType.

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

J'ai essayé ceci: Lorsque vous créez votre instance Retrofit, ajoutez cette fabrique de convertisseurs au constructeur Retrofit:

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

Vous pouvez utiliser hashmap si vous ne voulez pas créer de classe pojo pour chaque appel d'API.

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

Et puis envoie comme ça

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