วิธีการโพสต์ดิบ JSON ทั้งหมดในเนื้อความของคำขอการติดตั้งเพิ่ม?


284

อาจมีการถามคำถามนี้ก่อนหน้านี้ แต่ไม่ได้รับคำตอบอย่างชัดเจน หนึ่งโพสต์ดิบทั้งหมดของ JSON ภายในเนื้อความของคำขอการติดตั้งเพิ่มยังไง?

ดูคำถามที่คล้ายกันที่นี่ หรือเป็นคำตอบที่ถูกต้องนี้ว่ามันจะต้องมีรูปแบบเข้ารหัส URL และผ่านการเป็นเขตข้อมูล ? ฉันหวังว่าจะไม่จริง ๆ เพราะบริการที่ฉันกำลังเชื่อมต่ออยู่นั้นคาดหวังว่าจะมี JSON แบบดิบในเนื้อหาของโพสต์ พวกเขาไม่ได้ตั้งค่าให้ค้นหาเขตข้อมูลเฉพาะสำหรับข้อมูล JSON

ฉันแค่อยากจะอธิบายเรื่องนี้ให้ฟังด้วยซ้ำทุกครั้ง มีคนหนึ่งตอบว่าไม่ต้องใช้ชุดติดตั้งเพิ่ม อื่น ๆ ไม่แน่ใจของไวยากรณ์ อีกคนคิดว่าใช่มันสามารถทำได้ แต่เฉพาะเมื่อมีการเข้ารหัสรูปแบบ URL และวางในฟิลด์ (นั่นไม่ใช่กรณีที่ฉันยอมรับ) ไม่ฉันไม่สามารถกำหนดรหัสบริการทั้งหมดใหม่สำหรับไคลเอนต์ Android ของฉันได้ และใช่มันเป็นเรื่องธรรมดามากในโครงการสำคัญ ๆ ที่จะโพสต์ JSON แบบ raw แทนที่จะส่งผ่านเนื้อหา JSON เป็นค่าคุณสมบัติของฟิลด์ มาทำให้ถูกต้องแล้วเดินต่อไป บางคนสามารถชี้ไปที่เอกสารหรือตัวอย่างที่แสดงวิธีการนี้ได้หรือไม่ หรือระบุเหตุผลที่ถูกต้องว่าทำไมจึงไม่สามารถทำได้ / ไม่ควรทำ

ปรับปรุง: สิ่งหนึ่งที่ฉันสามารถพูดได้อย่างมั่นใจ 100% คุณสามารถทำได้ในวอลเลย์ของ Google มันถูกสร้างขึ้นมาเราสามารถทำสิ่งนี้ใน Retrofit ได้หรือไม่?


7
โพสต์ของ Jake Wharton ถูกต้องแล้ว! ทำเครื่องหมายว่าเป็นคำตอบ!
edotassi

1
คุณอาจใช้ jsonObject ดีกว่า
superUser

ทำงานอย่างสมบูรณ์แบบRequestBodyเช่นนี้ -> RequestBody body = RequestBody.create(MediaType.parse("text/plain"), text);สำหรับคำตอบโดยละเอียดfuturestud.io/tutorials/…
Kidus Tekeste

คำตอบ:


461

@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 ที่เกี่ยวข้อง" มีสองวิธีในการส่งข้อมูลดิบอย่างง่ายดายด้วยการประกาศข้างต้น:

  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. Subclass TypedStringเพื่อสร้างTypedJsonStringคลาส:

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

    จากนั้นใช้อินสแตนซ์ของคลาสนั้นคล้ายกับ # 1


5
อย่างไรก็ตามมีวิธีที่ดีที่จะทำสิ่งนี้โดยไม่ต้องทำ pojos?
superUser

28
สิ่งนี้ไม่ทำงานบนชุดติดตั้งเพิ่มเติม 2. คลาส TypedInput และ TypedString ถูกลบออก
Ahmed Hegazy

2
@jakewharton เราสามารถทำอะไรได้บ้างTypedStringเนื่องจากถูกลบไปแล้ว?
Jared Burrows

12
สำหรับ Retrofit2 คุณสามารถใช้RequestBodyเพื่อสร้างเนื้อหาดิบ
bnorm

4
ฉันได้รับการยกเว้นนี้java.lang.IllegalArgumentException: Unable to create @Body converter for class MatchAPIRequestBody (parameter #1)
Shajeel Afzal

155

แทนที่จะเป็นคลาสที่เราสามารถใช้โดยตรงHashMap<String, Object>เพื่อส่งพารามิเตอร์ร่างกายตัวอย่างเช่น

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

2
ในเวลานั้นคุณสามารถสร้างแผนที่ Hash เช่น HashMap <String, Object> สามารถสร้างอาร์เรย์ที่ซับซ้อนและวัตถุ JSON ได้
ผู้เรียน

2
นี่ยอดเยี่ยมถ้าคุณไม่ต้องการผูกติดกับ POJO บางชนิด
ทิม

2
@ ไม่มีคุณไม่สามารถส่งวัตถุ json โดยใช้ชุดติดตั้งเพิ่มเติม ... คุณยึดติดกับ pojo หรือคำตอบของฉัน ... นี่คือธรรมชาติของ retrofit.if ถ้าคุณต้องการข้อมูลเพิ่มเติมเกี่ยวกับคำถามนี้ Jake Wharton เขาเป็นคนพัฒนา retrofit คำตอบของเขายังสามารถใช้ได้กับ pojo .
ผู้เรียน

5
ฉันไปIllegalArgumentException: Unable to create @Body converter for java.util.HashMap<java.lang.String, java.lang.Object>กับ Moshi ฉันคิดว่า Gson จำเป็นสำหรับการทำงานนี้
osrl

2
หากใช้ Kotlin ให้ใช้ hashmap ของ <String, Any>
peresis ผู้ใช้

148

ใช่ฉันรู้ว่ามันสาย แต่บางคนอาจได้รับประโยชน์จากสิ่งนี้

ใช้ Retrofit2:

ฉันเจอปัญหานี้เมื่อคืนที่ผ่านมาการโยกย้ายจาก Volley ไปยัง Retrofit2 (และในฐานะ OP state สิ่งนี้ถูกสร้างขึ้นใน Volley ด้วยJsonObjectRequest) และแม้ว่าคำตอบของ Jake คือคำตอบที่ถูกต้องสำหรับ Retrofit1.9แต่ Retrofit2 ไม่มีTypedStringไม่ได้

กรณีของฉันจำเป็นต้องส่ง a Map<String,Object>ที่อาจมีค่า Null บางส่วนถูกแปลงเป็น JSONObject (ที่จะไม่บินไปด้วย@FieldMapไม่ต้องใช้ตัวอักษรพิเศษบางตัวแปลง) ดังนั้นทำตาม @bnorms hint และตามที่Squareระบุไว้:

สามารถระบุวัตถุเพื่อใช้เป็นเนื้อความคำขอ HTTP พร้อมด้วยคำอธิบายประกอบ @Body

วัตถุจะถูกแปลงโดยใช้ตัวแปลงที่ระบุในอินสแตนซ์ของชุดติดตั้งเพิ่มเติม หากไม่มีการเพิ่มตัวแปลงสามารถใช้ RequestBody ได้เท่านั้น

ดังนั้นนี่คือตัวเลือกที่ใช้RequestBodyและResponseBody :

ในส่วนต่อประสานของคุณใช้@BodyกับRequestBody

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

ในจุดโทรของคุณสร้าง a 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())

}

2
ใช่ฉันเห็นการตอบสนองที่ซับซ้อนมากมายสำหรับเรื่องนี้ หากคุณใช้ Retrofit2 และต้องการทำวอลเลย์JsonObjectRequestทุกสิ่งที่คุณต้องทำคือ คำตอบที่ดี.
VicVu

2
ชุดติดตั้งเพิ่มเพิ่มคีย์ชื่อ "nameValuePairs" ที่ด้านบนของวัตถุ json ทั้งหมด ฉันจะลบ @TommySM นี้ได้อย่างไร
nr5

1
ขอบคุณ! นี่เป็นการแก้ไขปัญหาของฉัน ตอนนี้ฉันสามารถส่ง JSONObject โดยตรงโดยไม่ต้องสร้าง POJO
Erfan GLMPR

1
นี่เป็นทางออกเดียวที่ช่วยให้ฉันไปpost a null valueยังสถานที่ให้บริการใน requestBody ซึ่งได้รับการเพิกเฉย
Shubhral

ฉันรู้ว่าฉันมาช้า แต่สิ่งที่อยู่jsonParams.put("code", some_code);ในบรรทัดที่สาม?
Naveen Niraula

81

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

10
เคล็ดลับที่นี่คืออะแดปเตอร์ Scalar ก่อน Gson มิฉะนั้น Gson จะตัด JSON ที่ต่อเนื่องกันเป็นชุดด้วยตนเองในสตริง
TWiStErRob

2
jonathan-nolasco-barrientosคุณต้องเปลี่ยน. baseUrl (ApiInterface.ENDPOINT) เป็น. baseUrl (ApiInterface.URL_BASE)
Milad Ahmadi

2
เมื่อคุณใช้GsonConverterFactoryที่.toString()ไม่จำเป็น คุณสามารถประกาศCall<User> getUser(@Body JsonObject body);ใช้JsonObjectแทนJSONObjectและส่งผ่านparamObjectโดยตรง มันจะทำงานได้ดี
Igor de Lorenzi

1
@IgordeLorenzi แก้ปัญหาของฉันเนื่องจากฉันใช้ spring boot เพื่อดึง json เท่านั้น JsonObject จาก gson ทำงานได้ดี
4259

1
@IgordeLorenzi มีความแตกต่างกันหรือไม่ว่า JSONObject หรือ JsonObject ที่ดีกว่าควรใช้กับ scalars
Sumit Shukla

44

การใช้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());
        }
    };

และนั่นมัน! ในความเห็นส่วนตัวของฉันมันดีกว่าการทำ pojos และทำงานกับระเบียบเรียน นี่สะอาดกว่าเยอะ


1
ถ้าฉันไม่ต้องการส่งค่า specif ในคลาส jsonobject หมายเหตุใดที่ฉันสามารถใช้ด้านบนที่ใช้ได้จริง
Ali Gürelli

1
ในขณะที่คุณสามารถดูตัวอย่างข้างต้น ... JsonObject มันเป็นวัตถุไม่ได้ใช้คำอธิบายประกอบ ในกรณีของคุณถ้าคุณไม่ต้องการส่งค่าเฉพาะคุณอาจไม่ได้เพิ่มเป็นคุณสมบัติ ...
superUser

1
ฉันหมายถึงฉันไม่ต้องการส่งค่าที่ประกาศในชั้นเรียน Btw ฉันแก้ไขปัญหาได้แล้ว มีคำอธิบายประกอบสำหรับชื่อที่เปิดเผย
Ali Gürelli

2
นี่เป็นวิธีที่ยืดหยุ่นที่สุด คุณสามารถสร้างวัตถุ json ของคุณแม้ว่าคุณจะไม่ทราบว่ามีกี่สาขาที่คุณจะมีหรือแม้ว่าคุณจะไม่รู้ว่าพวกเขาตั้งชื่อ +1 จากฉันหรือไม่
Stoycho Andreev

1
ฉันได้รับข้อผิดพลาดวิธีการบริการไม่สามารถคืนค่าเป็นโมฆะ สำหรับวิธี APIServices.signUpUser
Erum

11

ฉันชอบคำแนะนำของ Jake เกี่ยวกับTypedStringคลาสย่อยด้านบนโดยเฉพาะโดยเฉพาะ คุณสามารถสร้างคลาสย่อยที่หลากหลายได้ตามข้อมูล POST ที่คุณวางแผนที่จะผลักดันซึ่งแต่ละประเภทนั้นมีการปรับแต่งตามความต้องการของตัวเอง

คุณยังมีตัวเลือกในการเพิ่มคำอธิบายประกอบส่วนหัวในวิธีการ JSON POST ของคุณใน Retrofit API ของคุณ ...

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

… แต่การใช้คลาสย่อยนั้นเห็นได้ชัดกว่าการจัดทำเอกสารด้วยตนเอง

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

1
บันทึกวันด้วยตัวอย่างที่ชัดเจนโดยใช้ TypedJsonString จากข้อเสนอแนะ JW
miroslavign

10

1) เพิ่มการพึ่งพา -

 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 schema 2 pojo

โปรดจำไว้ว่า -
ภาษาเป้าหมาย: Java - ประเภทแหล่งที่มา: JSON - คำอธิบายรูปแบบ: Gson - เลือกรวม getters และ setters - นอกจากนี้คุณอาจเลือกอนุญาตคุณสมบัติเพิ่มเติม

http://www.jsonschema2pojo.org/

4) ทำให้การโทรไปๆมาๆ

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

9

ฉันพบว่าเมื่อคุณใช้วัตถุผสมเป็น@Bodyพารามิเตอร์มันไม่สามารถทำงานได้ดีกับ Retrofit's GSONConverter(ภายใต้สมมติฐานที่คุณใช้งานอยู่) คุณต้องใช้งานJsonObjectและไม่JSONObjectทำงานเมื่อใช้งานมันจะเพิ่มNameValueParamsโดยไม่ต้องใช้คำอธิบายใด ๆ - คุณจะเห็นได้ว่าหากคุณเพิ่มการพึ่งพาการบันทึก interceptor และ shenanigans อื่น

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>

2
อย่างนี้ช่วยฉันหลังจาก googling ข้ามคืน
W4R10CK

8

เพิ่ม 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 เป็นสตริงอย่าลืมเพิ่ม@Headers("Content-Type: application/json"):

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

ตอนนี้คุณสามารถโพสต์ json ดิบ


6

คุณสามารถใช้ 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);

5

ใช้การติดตามเพื่อส่ง 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

4

หลังจากใช้ความพยายามมากพบว่าความแตกต่างพื้นฐานคือคุณต้องส่งJsonObjectแทนที่จะJSONObjectเป็นพารามิเตอร์


ฉันก็ทำผิดพลาดเหมือนกัน: p
Mehtab Ahmed

4

จากคำตอบสูงสุดฉันมีทางออกที่จะไม่ต้องทำ 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 ได้


4

หากคุณไม่ต้องการที่จะสร้างชั้นเรียนพิเศษหรือใช้คุณสามารถใช้JSONObjectHashMap

ชุดติดตั้งเพิ่มเติม:

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

3

สิ่งที่จำเป็นในการส่ง raw json ใน Retrofit

1)ตรวจสอบให้แน่ใจว่าได้เพิ่มส่วนหัวต่อไปนี้และลบส่วนหัวที่ซ้ำกันอื่น ๆ ตั้งแต่บนเอกสารทางการของ Retrofit พวกเขากล่าวถึงเฉพาะ -

โปรดทราบว่าส่วนหัวไม่ได้เขียนทับกัน ส่วนหัวที่มีชื่อเดียวกันทั้งหมดจะรวมอยู่ในคำขอ

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

2) หากคุณใช้โรงงานแปลงคุณสามารถส่ง json ของคุณเป็น String, JSONObject, JsonObject และแม้แต่ POJO นอกจากนี้ยังมีการตรวจสอบไม่จำเป็นต้องทำงานเท่านั้นScalarConverterFactoryGsonConverterFactory

@POST("/urlPath")
@FormUrlEncoded
Call<Response> myApi(@Header("Authorization") String auth, @Header("KEY") String key, 
                     @Body JsonObject/POJO/String requestBody);

2) b. หากคุณไม่ได้ใช้ตัวแปลงจากโรงงานคุณต้องใช้ RequestBody ของ okhttp3 เป็นเอกสารเพิ่มเติมของ Retrofit

วัตถุจะถูกแปลงโดยใช้ตัวแปลงที่ระบุในอินสแตนซ์ของชุดติดตั้งเพิ่มเติม หากไม่มีการเพิ่มตัวแปลงสามารถใช้ RequestBody ได้เท่านั้น

RequestBody requestBody=RequestBody.create(MediaType.parse("application/json; charset=utf-8"),jsonString);

@POST("/urlPath")
@FormUrlEncoded
Call<Response> myApi(@Header("Authorization") String auth, @Header("KEY") String key, 
                 @Body RequestBody requestBody);

3)สำเร็จ !!


2

นี่คือสิ่งที่ใช้งานได้กับเวอร์ชันปัจจุบันretrofit 2.6.2 ,

ก่อนอื่นเราต้องเพิ่ม Scalars Converter ลงในรายการของ Gradle dependencies ของเราซึ่งจะคอยดูแลการแปลง java.lang.String ออบเจกต์เป็น text / plain request

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

จากนั้นเราจะต้องส่งโรงงานตัวแปลงไปยังตัวสร้าง Retrofit ของเรา ในภายหลังจะบอก Retrofit ถึงวิธีการแปลงพารามิเตอร์ @Body ที่ส่งผ่านไปยังบริการ

private val retrofitBuilder: Retrofit.Builder by lazy {
    Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(ScalarsConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
}

หมายเหตุ: ในเครื่องมือสร้างชุดติดตั้งเพิ่มเติมฉันมีตัวแปลงสองตัวGsonและ Scalarsคุณสามารถใช้ทั้งคู่ได้ แต่จะส่งตัว Json เราจำเป็นต้องโฟกัสScalarsดังนั้นหากคุณไม่ต้องการGsonลบออก

จากนั้นติดตั้งเพิ่มบริการด้วยพารามิเตอร์ String body

@Headers("Content-Type: application/json")
@POST("users")
fun saveUser(@Body   user: String): Response<MyResponse>

จากนั้นสร้างเนื้อความ JSON

val user = JsonObject()
 user.addProperty("id", 001)
 user.addProperty("name", "Name")

โทรหาบริการของคุณ

RetrofitService.myApi.saveUser(user.toString())

2

✅✅✅✅✅✅✅✅✅✅✅✅ วิธีการทำงาน ✅✅✅✅✅✅✅✅✅✅✅✅

ในขณะที่การสร้าง OkHttpClientที่จะใช้สำหรับการติดตั้งเพิ่ม

เพิ่ม Interceptor เช่นนี้

 private val httpClient = OkHttpClient.Builder()
        .addInterceptor (other interceptors)
        ........................................

        //This Interceptor is the main logging Interceptor
        .addInterceptor { chain ->
            val request = chain.request()
            val jsonObj = JSONObject(Gson().toJson(request))

            val requestBody = (jsonObj
            ?.getJSONObject("tags")
            ?.getJSONObject("class retrofit2.Invocation")
            ?.getJSONArray("arguments")?.get(0) ?: "").toString()
            val url = jsonObj?.getJSONObject("url")?.getString("url") ?: ""

            Timber.d("gsonrequest request url: $url")
            Timber.d("gsonrequest body :$requestBody")

            chain.proceed(request)
        }

        ..............
        // Add other configurations
        .build()

ตอนนี้ทุกสายติดตั้งเพิ่มของ URL ของคุณและขอร่างกายจะถูกบันทึกไว้Logcatใน กรองตาม"gsonrequest"


1

ฉันลองสิ่งนี้: เมื่อคุณสร้างอินสแตนซ์ของชุดติดตั้งเพิ่มตัวแปลงโรงงานนี้ลงในตัวสร้างชุดติดตั้งเพิ่มเติม:

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

1

แก้ไขปัญหาของฉันตามคำตอบของ 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();

0

เพื่อความชัดเจนมากขึ้นเกี่ยวกับคำตอบที่ได้รับที่นี่นี่คือวิธีที่คุณสามารถใช้ฟังก์ชันส่วนขยาย นี่เป็นเพียงถ้าคุณกำลังใช้ 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)

0

ฉันต้องการเปรียบเทียบความเร็วของวอลเลย์และชุดติดตั้งเพิ่มเติมสำหรับการส่งและรับข้อมูลที่ฉันเขียนด้านล่างโค้ด (สำหรับชุดติดตั้งเพิ่มเติม)

การพึ่งพาครั้งแรก:

dependencies {
     implementation 'com.squareup.retrofit2:retrofit:2.4.0'
     implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
}

จากนั้นอินเทอร์เฟซ:

 public interface IHttpRequest {

    String BaseUrl="https://example.com/api/";

    @POST("NewContract")
    Call<JsonElement> register(@Body HashMap registerApiPayload);
}

และฟังก์ชั่นเพื่อตั้งค่าพารามิเตอร์เพื่อโพสต์ข้อมูลไปยังเซิร์ฟเวอร์ (ใน MainActivity):

private void Retrofit(){

    Retrofit retrofitRequest = new Retrofit.Builder()
            .baseUrl(IHttpRequest.BaseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    // set data to send
    HashMap<String,String> SendData =new HashMap<>();
    SendData.put("token","XYXIUNJHJHJHGJHGJHGRTYTRY");
    SendData.put("contract_type","0");
    SendData.put("StopLess","37000");
    SendData.put("StopProfit","48000");

    final IHttpRequest request=retrofitRequest.create(IHttpRequest.class);

    request.register(SendData).enqueue(new Callback<JsonElement>() {
        @Override
        public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
            if (response.isSuccessful()){
                Toast.makeText(getApplicationContext(),response.body().toString(),Toast.LENGTH_LONG).show();
            }
        }

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

        }
    });

}

และฉันพบว่าชุดติดตั้งเพิ่มเร็วกว่าวอลเลย์ในกรณีของฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.