I am trying to send multiple parameters (as I usually do) with #QueryMap but via POST this time using retrofit.
Retrofit API
#POST("/request.php")
void sendRequest(#QueryMap Map<String, String> parameters, retrofit.Callback<RequestSendResponse> callback);
Map that is being send
public static Map<String, String> parametersSendRequest(Context sender, Request request)
{
Map <String, String> parameters = new HashMap<>();
Operator operator = AppConfig.config().operator;
parameters.put("user_name", request.user_name);
parameters.put("user_surname", request.user_surname);
parameters.put("user_gender", request.user_gender);
parameters.put("user_relationship", request.user_relationship);
parameters.put("user_dob", request.user_dob);
parameters.put("operator_name", operator.name);
parameters.put("request_photoid", request.request_photoid);
parameters.put("request_user_content", request.request_user_content);
parameters.put("request_title", request.request_title);
parameters.put("uuid", UUID(sender));
parameters.put("response_type", "json");
parameters.put("platform", "android");
parameters.put("mode", "send");
return parameters;
}
Server result
{"POST":[],"GET":{"operator_name":....}}
I can see that even the method is sent to POST, #QueryMap causes these parameters to be sent over GET. Even when I use #Body instead of #QueryMap, retrofit converts my #QueryMap to a JSON object, which is not I want.
All I want to do is to send param1=value1¶m2=value2 on my request body, instead of a JSON object (Using my Map<String, String>)
to send parameters using POST (#FormUrlEncoded and #FieldMap)
#FormUrlEncoded
#POST("/request.php")
void sendRequest(#FieldMap Map<String, String> parameters, retrofit.Callback<RequestSendResponse> callback);
This one works for me
#FormUrlEncoded
#POST("/profile/")
void getUserProfile(#Field("whatever")String whatever, Callback<Response> callback);
Pay special attention to the final slash after "profile". I had problems because I was not adding it. Hope it helps.
Related
I'm trying to make a PUTrequest using retrofit. All parameters sent except the data inside the Classic Java object. it contains a and other data but the backend not adding any of its parameters. I tried adding the HashMap as an individual part "working_session_pauses_attributes" but also not sent. Any solution or suggestion?
Thanks in advance
#Multipart
#PUT(WORKING_SESSION_PATH)
Observable<Response<WorkingSession>> updateWorkingSession(#Path(LOCATION_ID_VARIABLE) String locationId,
#Path(EMPLOYEE_ID_VARIABLE) String employeeId,
#Path(WORKING_SESSION_ID_VARIABLE) String workingSessionId,
#Part("working_session_id") String working_session_id,
#Part("ends_at") String ends_at,
#Part("starts_at") String starts_at,
#Part("secure_id") String secure_id,
#Part ("tag_ids[]") Long[] tag_ids,
#Part ("working_session_pauses_attributes") HashMap<Integer, UpdateBreakDataModel> working_session_pauses_attributes,
#Part ("data") CreateWorkingSessionRequestBody CreateWorkingSessionRequestBody,
#Part MultipartBody.Part end_signature,
#Part MultipartBody.Part start_signature_break,
#Part MultipartBody.Part end_signature_break);
I don't know what are you doing, you want post request but you are using put, please check. I am showing you my code how i am sending data using Hash map.
This is my method declaration:
#GET(ApiConstant.REQUEST_FORM_CHECK_OUT_ID)
Call<CheckOutIdResponseParent> callRequestFormCheckoutId(#QueryMap Map<String, String> params);
& here i am calling that method using hashmap.
Map<String, String> params = new HashMap<>();
params.put("category_id", categoryId);
params.put("currency", currency);
params.put("language", language);
ServiceApi mRetrofitCommonService = RetrofitClient.getInstance();
Call<ProductListParentResponse> call = mRetrofitCommonService.callProductListApi(params);
Retrofit converts POST in GET request when URL is like this
https://www.example.com/index.php?route=api/account/login
Found the solution
#FormUrlEncoded
#POST("index.php")
Call<String> login(#QueryMap(encoded=true) Map<String, String> options,#Field("email") String username,#Field("password") String password);
and calling will be like this
Map<String, String> map = new HashMap<>();
map.put("route","restapi/account/login");
Call<String> call = mAPIService.login(map, email, password);
I am using an API that uses an authorization scheme that requires a special "X-Authorization" header to be set to authenticate the request. For example, this Retrofit setup works perfectly for the user whose auth token is abc123:
#Headers("X-Authorization: abc123")
#GET("/posts")
Observable<List<Post>> get_posts();
I cache the user's X-Authorization token, so I have access to that, however, I can't just drop it in the #Headers declaration.
#Headers("X-Authorization: " + token)
#GET("/posts")
Observable<List<Post>> get_posts();
I get a compile error here: Error:(41, 34) error: element value must be a constant expression
Any ideas on how I could get around this?
Since Retrofit 2.0 you have two options
1) Using OkHttp 2.2+ use Interceptor
At the Http level, you have more control over the request, so you could do things like applying headers only to a specific request made to a specific endpoint, and so on.
public class MyOkHttpInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (!"/posts".contains(originalRequest.url()) ) {
return chain.proceed(originalRequest);
}
String token = // get token logic
Request newRequest = originalRequest.newBuilder()
.header("X-Authorization", token)
.build();
return chain.proceed(newRequest);
}
[...]
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.networkInterceptors().add(new MyOkHttpInterceptor());
OkClient okClient = new OkClient(okHttpClient);
YourApi api = new RestAdapter.Builder()
.setEndpoint(url)
.setClient(okClient)
.build()
.create(YourApi.class);
Edit:
Adding #JakeWarthon comment as another option as is also valid.
2) Put #Header on a method parameter and pass it as a value when invoking.
From the docs:
// Replaces the header with the the value of its target.
#GET("/")
void foo(#Header("Accept-Language") String lang, Callback<Response> cb);
Header parameters may be null which will omit them from the request. Passing a List or array will result in a header for each non-null item.
Note: Headers do not overwrite each other. All headers with the same name will be included in the request.
EDIT: This option should not be considered as Retrofit 2.* dropped support for interceptors.
3) User retrofit RequestInterceptor
From the docs:
Intercept every request before it is executed in order to add additional data.
You could do something like
public class MyRetrofitInterceptor implements RequestInterceptor {
#Override
public void intercept(RequestFacade req) {
String token = // get token logic
if (token != null) {
req.addHeader("X-Authorization", token);
}
}
[...]
YourApi api = new RestAdapter.Builder()
.setEndpoint(url)
.setRequestInterceptor(new MyRetrofitInterceptor())
.build()
.create(YourApi.class);
The "problem" with this approach is that the interceptor will get executed on all the endpoints, as it's set at the RestAdapter level, and not per endpoint. Also, the RequestFacade doesn't expose much information about the request, so no chance to add much logic around it.
Passing header in parameter would be helpful. Look to the following code;
#GET("/posts")
Observable<JsonElement> getDataFromService(
#HeaderMap Map<String, String> headers,
#QueryMap HashMap<String, Object> queryParams
);
hashMap1.put("Authorization", token);
return ApiService.getAPI_test().getDataFromService(hashMap1, url, hashMap)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
Update:
More better would be
#GET("/posts")
Observable<JsonElement> getDataFromService(
#Header("Authorization") token: String = "Bearer " + PreferenceUtils.getToken(),
#QueryMap HashMap<String, Object> queryParams
);
Dynamic Header In Retrofit 2
I have struggled too much to add Dynamic Header In Retrofit 2.
I have gone through so many blogs and StackOver flow. Everyone has shown example with Interceptor.
And it’s not a wise thing ,just for one API call we need to do that much work.
You just have to add #HeaderMap as argument of fun. I have done in very simple way :-
In Kotlin
val headers = HashMap<String, String>()
headers["KEY_AUTHORIZATION"] = "paste AUTHORIZATION value here"
headers["KEY_TOKEN"] = "paste TOKEN value here"
val jsonObject= JsonObject()
I am passing here header and other data also
Calling of fun:-
postEvent(headers,jsonObject)
API Declaration
#POST("/v1/post_data")
fun postEvent(#HeaderMap headers: Map<String, String>, #Body jsonObject: JsonObject): Call<JsonObject>
API Declaration with RxAndroid
#POST("/v1/post_data")
fun postEvent(#HeaderMap headers: Map<String, String>, #Body jsonObject: JsonObject): Single<JsonObject>
2nd argument here i have JsonObject. You can replace with anything whatever you need to pass or you can remove it also.
In Java
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("KEY_AUTHORIZATION","paste AUTHORIZATION value here");
headers.put("KEY_TOKEN", "paste TOKEN value here");
JsonObject jsonObject= new JsonObject();
I am passing here header and other data also
Calling of fun:-
postEvent(headers,jsonObject);
API Declaration
#POST("/v1/post_data")
Call<JsonObject> postEvent(#HeaderMap Map<String, String> headers, #Body JsonObject jsonObject);
API Declaration with RxAndroid
#POST("/v1/post_data")
Single<JsonObject> postEvent(#HeaderMap Map<String, String> headers, #Body JsonObject jsonObject);
2nd argument here i have JsonObject. You can replace with anything whatever you need to pass or you can remove it also.
A request Header can be updated dynamically using the #Header annotation. A corresponding parameter must be provided to the #Header. If the value is null, the header will be omitted. Otherwise, toString will be called on the value, and the result used.
#GET("user")
Call<User> getUser(#Header("Authorization") String authorization)
When the last part of this answer
How to dynamically set headers in Retrofit (Android)
did not work for me (halfway of the project), I improved it:-
public class MyRetrofitInterceptor implements RequestInterceptor {
// volatile variable
public static String token = null; //change at start of app
#Override
public void intercept(RequestFacade req) {
// change token from outside the class.
if (token != null) {
req.addHeader("X-Authorization", token);
}
}
It worked as soon as the token was updated from the response from the server API.
I think it worked as the string variable 'token' was used as the reference to its value, in global terms (being public static).
I'm trying to send a JSON as string (not as object) to the server (in this case it's a WebAPI). I always get a error code 500.
I succeeded to get response from the server when the request was GET and without sending data to the server. this achieved by JsonObjectRequest.
Now, I trying to send a POST request with JSON as string. For that I try
JsonObjectRequest
StringRequest
GsonRequest
JsonRequest - here I supplied my json in the requestBody
Before using volley, I used other methods to send request to server which require to simply build an object, serialized to json (string) and pass via StringEntity.
I can't understand where should I pass the json in the request. or what I'm doing wrong.
I don't exactly understand why do you want to send the JSON as a string and not as an object. Nevertheless, in your WebAPI endpoint you should put a breakpoint in the Post method of the ApiController and see whether the request gets there or not.
Probably, you're mixing the content-types of the request. If you want to send a simple string request from Volley, you should just use the StringRequest and send there the JSON text. Thus, in the WebAPI POST method you must get the string without being deserialized to JSON. I answered once a similar question of how this string request should be made here.
However, as I said before I would suggest using always JSON requests which includes the contentType:"application/json" header, and receive requests in the WebAPI deserialized.
url = "yoururl"; StringRequest postRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>()
{
#Override
public void onResponse(String response) {
// response
Log.d("Response", response);
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error) {
// error
Log.d("Error.Response", response);
}
} ) {
#Override
protected Map<String, String> getParams()
{
Map<String, String> params = new HashMap<String, String>();
params.put("your_field", youJSONObject.toString());
return params;
} }; queue.add(postRequest);
Try in this way to make the post (it should work with json obj or array)
I am working on volley library: http://developer.android.com/training/volley/index.html
Get and 'Post methods without parameters' working fine. But when parameters are given, volley does not execute the form, and acts like form itself is a jsonObject:
com.android.volley.ParseError: org.json.JSONException: Value Login< of type java.lang.String cannot be converted to JSONObject
I have tried both overriding getParams() method:
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("username", username);
params.put("password", password);
return params;
}
And instantiating the object with parameter:
Map<String, String> params2 = new HashMap<String, String>();
params2.put("username", username);
params2.put("password", password);
JsonObjectRequest jsonObjectRequest1 = new JsonObjectRequest(Request.Method.POST, LOGIN_URL, new JSONObject(params2), new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//onResponse
}
});
None of them worked. I am guessing my problem is about the content types. Volley library uses application/json while my php codes use name-value pairs.
I have seen these two questions but sadly they did not solve my case:
Google Volley ignores POST-Parameter
Volley Post JsonObjectRequest ignoring parameters while using getHeader and getParams
When you use JsonObjectRequest, you are saying that the content you are posting is a JSON Object and the response you expect back will also be a JSONObject. If neither of these are true, you need to build your own Request<T> and set the values you need.
The error you are seeing is because the response from the server is not a valid JSON response.