Send a Post with url parameter with Retrofit 2 - java

I want to send a POST with Retrofit 2. The url has some parameters:
#Headers({
"Accept: application/x-www-form-urlencoded;",
"User-Agent: my-app"
})
#FormUrlEncoded
#POST("server/directory/location.type")
`public Call<POJOStringValue> dataWithUr(#Path("arg1") String arg1, #Path("arg2"), String arg2);
The url looks like this
www.website.com/server/directory/location.type?arg1=value1&arg2=value2
I was requested to use a POST request. The values (value1 and value2) are dynamic at runtime. I started the project with Xamarin using HttpClient and now I'm rewriting it in Java native. In C# all I had to do was to concact the strings and send the resulting string in a single Post.
I tried to use #Path and the error was :
"server/directory/location.type" does not contain "{arg1}". (parameter #1)
Then, I tried to use #Query and the error was:
java.lang.IllegalArgumentException: Form-encoded method must contain at least one #Field.
Finally I tried with #Field the request never gets any response (I sette the connection timeout to 5 seconds)
Please help me, or tell me if I have to don't have any other choice but to use a GET request.
((EDIT))
Here is my code for the setup of the client:
private static void setupClient(){
final OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
//define retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(iXUtils.getUrl_())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
this.client_ = retrofit.create(RequestInterface.class);
}
The get() method:
public static RequestInterface get(){
return this.client_;
}
Here is how I call it:
public String callFunctionDB(String arg1, String arg2){
setupClient();
Call<POJOStringValue> call = get().dataWithUrlString(arg1, arg2);
try {
POJOStringValue response = call.execute().body();
String value = response.getValue();
int test = 0;
} catch (IOException e) {
String value = "it failded";
e.printStackTrace();
}
return "test";
}
I put the test=0 to be able to put a breaking point, it never gets there. Plus I called the method "callFunctionDB" in a doInbackground to avoid the android.os.NetworkOnMainThreadException.

Retrofit requires you to have at least one form parameter if you request form encoding. You have answered your own question -- you are using query parameters instead of POST fields, so that annotation is not necessary. Remove the #FormUrlEncoded annotation, and change your parameters to #Query annotations.

Related

Retrofit default query parameter [duplicate]

This question already has answers here:
Is there a way to add query parameter to every request with Retrofit 2?
(7 answers)
Closed 1 year ago.
I have an apikey "token" that has to be provided as a query parameter in every request.
How can i setup retrofit as to always include this query parameter in the request?
I am currently providing the token as a default parameter:
interface CompanyService {
#GET("accounts/{id}")
fun getCompany(
#Path("id") id: Number,
#Query("token") token: String = Constants.TOKEN) <---- here
: Call<CompanyResponse>
#GET("accounts/")
fun getCompanies(
#Query("id") page: String,
#Query("limit") limit: Int,
#Query("sort") sort: String = "id",
#Query("token") token: String = Constants.TOKEN) <---- here
: Call<CompanyListResponse>
}
But since i know that every request requires the same token, it feels redundant to have to provide it as a default parameter in every request function i create. How can i setup retrofit to decorate every outgoing request with a default query parameter?
This is how i build the retrofit2 instance:
class CompanyAPI {
companion object {
private var retrofit: Retrofit? = null
val client:Retrofit get() {
if (retrofit == null) {
retrofit=Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(Constants.BASE_URL)
.build()
}
return retrofit!!
}
}
}
I would think that you would add some additional function call in the call chain before calling .build() but looking at the available functions i can't say i see anything that would provide this functionality.
add interceptor to your http client with your default params or headers here is example for how to add it :-
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain)
throws IOException {
Request original = chain.request();
HttpUrl originalHttpUrl = original.url();
HttpUrl url = originalHttpUrl.newBuilder()
.addQueryParameter("apikey", "your-actual-api-key")
.build();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.url(url);
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
refrence
You need to add an interceptor to it & pass the client in your Builder like the following:
retrofit=Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(Constants.BASE_URL)
.client(
OkHttpClient.Builder()
.addInterceptor { chain ->
val url = chain
.request()
.url()
.newBuilder()
.addQueryParameter("token", Constants.TOKEN)
.build()
chain.proceed(chain.request().newBuilder().url(url).build())
}
.build()
)
.build()

How to send Retrofit POST body as JSON String?

I have a service like below. I call the method Retroifit library. The service works when I send the paramter with #Body Test type but I want work with #Body String that i don't create a Test class and using this in #Body. I create a JSONObject and convert this to String but in this way, the program doesn't work! Can you help me or suggest a solution for this?
My Web API:
[Route("TestService"), HttpPost, IgnoreDataLog]
public async Task<Result<TestResult>> Add(Test pmDeviceObj)
{
var listResult = await pmService.AddAsync(pmDeviceObj);
return listResult;
}
Android part:
#POST("TestService")
Call<Result<TestResult>> TestService(#Header("Content-Type") String content_type,#Body String body);
Call Service in Android-> I get the StatusCode 400 with the below code
JSONObject jsonBody=new JSONObject();
try {
jsonBody.put("Id",73);
jsonBody.put("seri","55656573");
jsonBody.put("code","fc24009b9160");
jsonBody.put("sID",8);
}catch (JSONException ex){
ex.printStackTrace();
}
retrofit2.Call<Result<TestResult>> call1=service.TestService("application/json",jsonBody.toString());
If I use the below code in the Android part, everything works correct and I take the data.
#POST("TestService")
Call<Result<TestResult>> TestService(#Header("Content-Type") String content_type,#Body Test inputValue);
Test test=new Test(73,"556565","fc24009b9160",8);
retrofit2.Call<Result<TestResult>> call1=service.TestService("application/json",test);
This happens because Retrofit considers String as a 'normal' object that has to be converted to a JSON and is not aware that it already is a JSON represntation of object.
If you have HttpLogginInterceptor configured, you should see that (simplified example) your JSON string:
{"sId": "8"}
actually goes like:
"{\"sId\": \"8\"}"
To prevent this happen you need to use something like the comment suggests ScalarsConverterFactory. First you need to set dependencies for it:
For Gradle
dependencies {
implementation “com.squareup.retrofit2:converter-scalars:2.4.0”
}
or for Maven
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-scalars</artifactId>
<version>2.4.0</version>
</dependency>
After done that you need to add the converter factory to your Retrofit, like (added also logging to be easily tested):
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor).build();
retrofit = new Retrofit.Builder()
.baseUrl(MY_URL)
// be sure to add this before gsonconverterfactory!
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory
.create())
.client(client).build();
myApi = retrofit.create(MY_API_CLASS);

Unable to fetch JSON as String from a REST API

I am trying to fetch JSON from a REST API as a string.
I am using retrofit with scalarConverter for that purpose. I am able to pass the URL to fetch and my retrofit instance is also created successfully but I am not getting any response from the server.
PS: there is no request on the server, so that means my request is not going out from my machine.
I am new to android, kindly help me.
Retrofit instance creation:
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(base)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
jsonApi jsonapi=retrofit.create(jsonApi.class);
Call<String> stringcall=jsonapi.getStringResponse(speech);
jsonApi interface:
public interface jsonApi {
#GET
Call<String> getStringResponse(#Url String url);
}
base: it is the base URL
speech: it is a variable containing rest of the URL to be processed.
when I run, the app gets stuck here with this message being displayed in Run Tab:
W/OpenGLRenderer: Fail to change FontRenderer cache size, it already initialized
W/art: Before Android 4.1, method int android.support.v7.widget.DropDownListView.lookForSelectablePosition(int, boolean) would have incorrectly overridden the package-private method in android.widget.ListView
With the below line
Call<String> stringcall=jsonapi.getStringResponse(speech);
you just get a Call object which is a representation of the HTTP request, and just by that, the request isn't executed. You need to use this object and call the execute method to make a synchronous request or the enqueue method to make an asynchronous request.
So in case you want to make a sync request, try the below:
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(base)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
jsonApi jsonapi=retrofit.create(jsonApi.class);
Call<String> stringcall=jsonapi.getStringResponse(speech);
try {
Response<String> response = stringcall.execute();
String result = response.body();
} catch (Exception ex) {
//handle exception
}
The documentation of Call interface is here, for your reference.

API call with Java + STS returning "Content type 'application/octet-stream' not supported"

I am working on part of an API, which requires making a call to another external API to retrieve data for one of its functions. The call was returning an HTTP 500 error, with description "Content type 'application/octet-stream' not supported." The call is expected to return a type of 'application/json."
I found that this is because the response received doesn't explicitly specify a content type in its header, even though its content is formatted as JSON, so my API defaulted to assuming it was an octet stream.
The problem is, I'm not sure how to adjust for this. How would I get my API to treat the data it receives from the other API as an application/json even if the other API doesn't specify a content type? Changing the other API to include a contenttype attribute in its response is infeasible.
Code:
The API class:
#RestController
#RequestMapping(path={Constants.API_DISPATCH_PROFILE_CONTEXT_PATH},produces = {MediaType.APPLICATION_JSON_VALUE})
public class GetProfileApi {
#Autowired
private GetProfile GetProfile;
#GetMapping(path = {"/{id}"})
public Mono<GetProfileResponse> getProfile(#Valid #PathVariable String id){
return GetProfile.getDispatchProfile(id);
}
The service calling the external API:
#Autowired
private RestClient restClient;
#Value("${dispatch.api.get_profile}")
private String getDispatchProfileUrl;
#Override
public Mono<GetProfileResponse> getDispatchProfile(String id) {
return Mono.just(id)
.flatMap(aLong -> {
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
return restClient.get(getDispatchProfileUrl, headers);
}).flatMap(clientResponse -> {
HttpStatus status = clientResponse.statusCode();
log.info("HTTP Status : {}", status.value());
return clientResponse.bodyToMono(GetProfileClientResponse.class);
// the code does not get past the above line before returning the error
}).map(GetProfileClientResponse -> {
log.debug("Response : {}",GetProfileClientResponse);
String id = GetProfileClientResponse.getId();
log.info("SubscriberResponse Code : {}",id);
return GetProfileResponse.builder()
// builder call to be completed later
.build();
});
}
The GET method for the RestClient:
public <T> Mono<ClientResponse> get(String baseURL, MultiValueMap<String,String> headers){
log.info("Executing REST GET method for URL : {}",baseURL);
WebClient client = WebClient.builder()
.baseUrl(baseURL)
.defaultHeaders(httpHeaders -> httpHeaders.addAll(headers))
.build();
return client.get()
.exchange();
}
One solution I had attempted was setting produces= {MediaType.APPLICATION_JSON_VALUE} in the #RequestMapping of the API to produces= {MediaType.APPLICATION_OCTET_STREAM_VALUE}, but this caused a different error, HTTP 406 Not Acceptable. I found that the server could not give the client the data in a representation that was requested, but I could not figure out how to correct it.
How would I be able to treat the response as JSON successfully even though it does not come with a content type?
Hopefully I have framed my question well enough, I've kinda been thrust into this and I'm still trying to figure out what's going on.
Are u using jackson library or jaxb library for marshalling/unmarshalling?
Try annotating Mono entity class with #XmlRootElement and see what happens.

Java: Android: Retrofit - using Call but, Response{code = 401,message=unauthorized}

trying to use Retrofit to access stuff, with themoviedatabase API, but i'm getting a crash, without any thrown exception or error message... I'm new to Retrofit, but i searched some documentation, and this is what i have(i'm using the Retrofit 2.0):
String movieToSearch = "fight";
String ENDPOINT = "https://api.themoviedb.org/3";
String API_KEY = "&api_key=------------------------------";
Retrofit adapter = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.build();
TMDBAPI apiService = adapter.create(TMDBAPI.class);
String query = movieToSearch + API_KEY;
Call<List<Movie>> call = apiService.getMovieList(query);
call.enqueue(new Callback<List<Movie>>() {
#Override
public void onResponse(Response<List<Movie>> response, Retrofit retrofit) {
List<Movie> movieList = (response.body());
}
#Override
public void onFailure(Throwable t) {
}
});
What am i doing worng here? :/
[EDIT] i added a / to the end point, and changed the method in the interface to this:
#GET("search/movie")
Call<List<Movie>> getMovieList( #Query("query") String query);
the problem now is, the response has body = null, in the rawResponse, it has a message saying =
Response{protocol=http/1.1, code=401, message=Unauthorized, url=https://api.themoviedb.org/3/search/movie?query=fight%26api_key%-----
do i have to set up a client?
Ok, I can see your problem, the search should be do like this:
http://api.themoviedb.org/3/search/movie?api_key=###&query=iron sky
So, the problem is how are you forming the URL.
I Figured what i was doing wrong, the result off the request i am making, gives more objects in the json... Just create an object with said fields, and with a List.

Categories