Volley to Retrofit2 - How to do a String Request with Retrofit2? - android

I would like to migrate to Retrofit2 the following Volley string request.
This request retrieves a response body as String, which I parse myself.
fun updatePodcastEpisodesRQ( url: String) {
val feedReq = StringRequestUTF8(
Request.Method.GET,
url,
{ response: String? -> ...},
{ error: VolleyError ->...}
)
App.instance?.addToRequestQueue(feedReq, TAG_JSON_REQUEST1)
}
Please note that the URL can be any address, as a result there is no baseUrl as defined in Retrofit.Builder() when doing JSON request for example.
Is it possible to do such a simple request with Retrofit2 ?

In fact, okhttp3 fulfilled all my needs. I migrated from Volley to okhttp3.
private val client = OkHttpClient()
suspend fun getFeed(url: String): String {
val request = Request.Builder()
.url(url)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful)
throw IOException("Error occurred - code:${response.code} message=${response.message}")
if (response.body == null)
throw IOException("Error occurred - null body")
return response.body!!.string()
}
}

Related

Not getting plain text response using Cronet Engine with Brotli enabled

I am using CronetEngine for making a network request as below
val engine: CronetEngine = CronetEngine.Builder(context).enableQuic(false)
.enableBrotli(true)
.enableHttp2(false).build()
val callFactory: Call.Factory = CronetCallFactory.newBuilder(engine).build()
And calling the result as
request = Request.Builder()
.url(url)
.post(formBody.build())
.headers(headers)
.build()
callFactory.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
result = response.body?.string()
mResponseCode = response.code
mResponseHeader = response.headers.toMultimap()
}
})
I am getting the response in compressed format.
Where as I want it in plain text or decompressed format.
Any configuration I am missing here?

Troubles with OkHttpClient() POST not working KOTLIN

I am attempting to make a sync call that needs to complete before proceeding with storing a user in the cloud. I believe the issue is within the RequestBody as it looks like it is just a byte array. Below is the code:
val client = OkHttpClient()
val mediaType: MediaType? = "application/json".toMediaTypeOrNull()
val body: RequestBody =
RequestBody.create(mediaType, "{\"type\":\"DEFAULT\",\"name\":\"lkjlkj\"}")
val request: Request = okhttp3.Request.Builder()
.url("https://api.example.com/endpoint")
.post(body)
.addHeader("Accept", "application/json")
.addHeader("Content-Type", "application/json")
.addHeader(
"Authorization",
"Bearer SK-xxxxxx-4QAXH"
)
.build()
Toast.makeText(this#RegisterActivity, "Entering Call",Toast.LENGTH_SHORT).show()
val response: Unit = client.newCall(request).execute().use {
Toast.makeText(this#RegisterActivity, "sent call, awaiting response",Toast.LENGTH_SHORT).show()
if (it.isSuccessful){
val content = JSONObject(it.body.toString())
desiredString = content.getJSONArray("desiredStringField").toString()
Toast.makeText(this#RegisterActivity, desiredString,Toast.LENGTH_SHORT).show()
}
if (!it.isSuccessful){
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
}
}
The code doesn't crash but it seems the call never completes, as it never makes it into the it.isSuccessful or !it.isSuccessful. Perhaps its a bad formed call somehow. Please help if you can.
Try to enqueue the request and manage the response using a Callback:
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful){
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
return
}
try {
val content = JSONObject(response.body?.string() ?: "")
desiredString = content.getJSONArray("desiredStringField").toString()
Toast.makeText(this#RegisterActivity, desiredString,Toast.LENGTH_SHORT).show()
} catch (e: JSONException) {
// Error parsing JSON object
}
}
override fun onFailure(call: Call, e: IOException) {
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
}
}

Retrofit put method call update the item but give 400 response code in error in android

I am using Retrofit to update some information. The information is updated in the database. But i am getting 400 error code in response. At the same time the API works perfectly in postman.
I have double-checked that I'm sending the required headers and the API token which updated on every login. But I'm get 400 error still, while the information is updated.
this is
You are getting 401 as a status code that means unauthorized token you are passing or something wrong with the auth token
Check auth token you are passing is correct or not or you are passing it or not.
If you are not passing auth token in the api header then please pass it will resolve your error
this is api module class
var token = ""
if (prefs.contains(Constants.TOKEN_VALUE)) {
prefs.read(Constants.TOKEN_VALUE)?.let {
token = it
}
}
val httpInterceptor = HttpLoggingInterceptor()
httpInterceptor.level = httpLoggingLevel
val okHttp = OkHttpClient.Builder()
.addInterceptor(httpInterceptor).addInterceptor { chain ->
val requestBuilder = chain.request().newBuilder()
requestBuilder.addHeader("Accept", "application/json")
requestBuilder.addHeader("Cache-Control", "no-cache")
requestBuilder.addHeader("Content-Type", "application/x-www-form-urlencoded")
if (token.isNotEmpty() && Constants.apitokenheader==0) {
requestBuilder.addHeader("Authorization", token)
Log.d("apitoken", "providesBaseApiService: $token")
}
chain.proceed(requestBuilder.build())
}
.addInterceptor { chain ->
val request = chain.request()
val response = chain.proceed(request)
when(response.code()){
200, 201 -> response
204 -> response.newBuilder().code(200).body(ResponseBody.create(MediaType.get("application/json"), "1")).build()
else -> {
try {
response.body()?.byteStream()?.readBytes()?.toString(Charset.defaultCharset())?.let {
val obj = JSONObject(it)
val opt = obj.optString("message", "An error occurred, Please try again.")
Log.v("error message", opt)
Log.v("error message1", request.url().toString())
Log.v("error message2", response.code().toString())
val link = request.url().uri().toString()
val sub : String = link.substringAfterLast("v1")
Log.v("DripInventory", sub)
Log.v("DripInventory", link.indexOf("v1").let { if (it == -1) null else link.substring(it + 1) })
// link.indexOf("v1").let { if (it == -1) null else link.substring(it + 1) }
//response.newBuilder().code(422).message(opt).build()
response.newBuilder().code(response.code().toInt())
.message("Please contact Drip Inventory." +
"\n " +
"\nResponse Code: ${response.code()}" +
"\n " +
"\nApi Call: $sub")
.build()
}
}catch (e: Exception){
response
}
}
}
/*if (response.code() == 204) {
response.newBuilder().code(200).body(ResponseBody.create(MediaType.get("application/json"), "1")).build()
} else {
response
}*/
}
.hostnameVerifier{hostname, session ->
if (hostname == "dripinventory.com") return#hostnameVerifier true
if (hostname == "invalid.demo.dripinventory.com") return#hostnameVerifier true
Log.v("hostname", hostname)
true
}
.build()
val gson = GsonBuilder()
// Serializers
.registerTypeAdapter(CreateAssetRequest::class.java, CreateAssetRequestSerializer())
.registerTypeAdapter(UpdateAssetRequest::class.java, UpdateAssetRequestSerializer())
// Deserializers
.registerTypeAdapter(AssetRaw::class.java, AssetRawDeserializer())
.create()
val retrofit = Retrofit.Builder()
.client(okHttp)
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
return retrofit.create(CcAssetManagerApi::class.java)

How can I get data from response body?

I need to get "status" value from my response. I tried JSONObject but it gave that java.lang.reflect.InvocationTargetException error in the code below. I need that because I make some process with status value like start new activity etc. How can I get this?
private fun doReq(){
val client = OkHttpClient().newBuilder()
.build()
val mediaType = "application/json;charset=utf-8".toMediaTypeOrNull()
val body: RequestBody = jsonSended.toRequestBody(mediaType)
val request: Request = Request.Builder()
.url("https://apiapiapi/auth")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "$authValue")
.addHeader("x-iyzi-rnd", "$randomString")
.addHeader("cache-control", "no-cache")
.build()
val response = client.newCall(request).execute()
println(response.body?.string()) // I CAN GET RESPONSE BODY
var json = JSONObject(response.body.toString())
var status = json.getString("status") // HERE NOT WORKING...
println(status)
}
In the response when server return failure : {"status":"failure","errorCode":"12","errorMessage":"invalid","locale":"tr","systemTime":1610022545347,"conversationId":"123412341234"}
If server return valid my response is very very long but it also have status so I can get that status value and do something with that. Is that correct way to do it? I need advice.
Use response.response or make request in try catch block statement, it will solve the problem
Something like this
private fun doReq(){
try {
var errorResponce: ErrorResponce? = null
val client = OkHttpClient().newBuilder()
.build()
val mediaType = "application/json;charset=utf-8".toMediaTypeOrNull()
val body: RequestBody = jsonSended.toRequestBody(mediaType)
val request: Request = Request.Builder()
.url("https://apiapiapi/auth")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "$authValue")
.addHeader("x-iyzi-rnd", "$randomString")
.addHeader("cache-control", "no-cache")
.build()
val response = client.newCall(request).execute()
println(response.body?.string()) // I CAN GET RESPONSE BODY
if (response.errorBody() != null) {
errorResponce = conversorDeErro.convert(response.errorBody()!!)
}
}
catch (e: IOException) {
println(e);
}
finally {
return errorResponce;
}
}
You should read response.body?.string() exactly once and store the result.
val response = client.newCall(request).execute()
println(response.body?.string()) // This permanently consumes the output, you should store the result
var json = JSONObject(response.body.toString()) // toString() is not the response body, it is a debug representation of the response body
var status = json.getString("status")

HTTP Request in Android with Kotlin

I want to do a login validation using POST method and to get some information using GET method.
I've URL, server Username and Password already of my previous project.
For Android, Volley is a good place to get started. For all platforms, you might also want to check out ktor client or http4k which are both good libraries.
However, you can also use standard Java libraries like java.net.HttpURLConnection
which is part of the Java SDK:
fun sendGet() {
val url = URL("http://www.google.com/")
with(url.openConnection() as HttpURLConnection) {
requestMethod = "GET" // optional default is GET
println("\nSent 'GET' request to URL : $url; Response Code : $responseCode")
inputStream.bufferedReader().use {
it.lines().forEach { line ->
println(line)
}
}
}
}
Or simpler:
URL("https://google.com").readText()
Send HTTP POST/GET request with parameters using HttpURLConnection :
POST with Parameters:
fun sendPostRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Your API Link>")
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "POST"
val wr = OutputStreamWriter(getOutputStream());
wr.write(reqParam);
wr.flush();
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
println("Response : $response")
}
}
}
GET with Parameters:
fun sendGetRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Yout API Link>?"+reqParam)
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "GET"
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
println("Response : $response")
}
}
}
Using only the standard library with minimal code!
thread {
val json = try {
URL(url).readText()
} catch (e: Exception) {
return#thread
}
runOnUiThread { displayOrWhatever(json) }
}
This starts a GET request on a new thread, leaving the UI thread to respond to user input. However, we can only modify UI elements from the main/UI thread, so we actually need a runOnUiThread block to show the result to our user. This enqueues our display code to be run on the UI thread soon.
The try/catch is there so your app won't crash if you make a request with your phone's internet off. Add your own error handling (e.g. showing a Toast) as you please.
.readText() is not part of the java.net.URL class but a Kotlin extension method, Kotlin "glues" this method onto URL. This is enough for plain GET requests, but for more control and POST requests you need something like the Fuel library.
Have a look at Fuel library, a sample GET request
"https://httpbin.org/get"
.httpGet()
.responseString { request, response, result ->
when (result) {
is Result.Failure -> {
val ex = result.getException()
}
is Result.Success -> {
val data = result.get()
}
}
}
// You can also use Fuel.get("https://httpbin.org/get").responseString { ... }
// You can also use FuelManager.instance.get("...").responseString { ... }
A sample POST request
Fuel.post("https://httpbin.org/post")
.jsonBody("{ \"foo\" : \"bar\" }")
.also { println(it) }
.response { result -> }
Their documentation can be found here
​
I think using okhttp is the easiest solution. Here you can see an example for POST method, sending a json, and with auth.
val url = "https://example.com/endpoint"
val client = OkHttpClient()
val JSON = MediaType.get("application/json; charset=utf-8")
val body = RequestBody.create(JSON, "{\"data\":\"$data\"}")
val request = Request.Builder()
.addHeader("Authorization", "Bearer $token")
.url(url)
.post(body)
.build()
val response = client . newCall (request).execute()
println(response.request())
println(response.body()!!.string())
Remember to add this dependency to your project https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
UPDATE: July 7th, 2019
I'm gonna give two examples using latest Kotlin (1.3.41), OkHttp (4.0.0) and Jackson (2.9.9).
UPDATE: January 25th, 2021
Everything is okay with the most updated versions.
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.12.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
Get Method
fun get() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users?page=2")
val request = Request.Builder()
.url(url)
.get()
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val mapperAll = ObjectMapper()
val objData = mapperAll.readTree(responseBody)
objData.get("data").forEachIndexed { index, jsonNode ->
println("$index $jsonNode")
}
}
POST Method
fun post() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users")
//just a string
var jsonString = "{\"name\": \"Rolando\", \"job\": \"Fakeador\"}"
//or using jackson
val mapperAll = ObjectMapper()
val jacksonObj = mapperAll.createObjectNode()
jacksonObj.put("name", "Rolando")
jacksonObj.put("job", "Fakeador")
val jacksonString = jacksonObj.toString()
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = jacksonString.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(body)
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val objData = mapperAll.readTree(responseBody)
println("My name is " + objData.get("name").textValue() + ", and I'm a " + objData.get("job").textValue() + ".")
}
Maybe the simplest GET
For everybody stuck with NetworkOnMainThreadException for the other solutions: use AsyncTask or, even shorter, (yet still experimental) Coroutines:
launch {
val jsonStr = URL("url").readText()
}
If you need to test with plain http don't forget to add to your manifest:
android:usesCleartextTraffic="true"
For the experimental Coroutines you have to add to build.gradle as of 10/10/2018:
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.24.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.24.0"
...
If you are using Kotlin, you might as well keep your code as succinct as possible. The run method turns the receiver into this and returns the value of the block.
this as HttpURLConnection creates a smart cast. bufferedReader().readText() avoids a bunch of boilerplate code.
return URL(url).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
You can also wrap this into an extension function.
fun URL.getText(): String {
return openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
And call it like this
return URL(url).getText()
Finally, if you are super lazy, you can extend the String class instead.
fun String.getUrlText(): String {
return URL(this).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
}
And call it like this
return "http://somewhere.com".getUrlText()
You can use kohttp library. It is a Kotlin DSL HTTP client. It supports the features of square.okhttp and provides a clear DSL for them. KoHttp async calls are powered by coroutines.
httpGet extension function
val response: Response = "https://google.com/search?q=iphone".httpGet()
you can also use async call with coroutines
val response: Deferred<Response> = "https://google.com/search?q=iphone".asyncHttpGet()
or DSL function for more complex requests
val response: Response = httpGet {
host = "google.com"
path = "/search"
param {
"q" to "iphone"
"safe" to "off"
}
}
You can find more details in docs
To get it with gradle use
implementation 'io.github.rybalkinsd:kohttp:0.12.0'
Without adding additional dependencies, this works. You don't need Volley for this. This works using the current version of Kotlin as of Dec 2018: Kotlin 1.3.10
If using Android Studio, you'll need to add this declaration in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
You should manually declare imports here. The auto-import tool caused me many conflicts.:
import android.os.AsyncTask
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection
You can't perform network requests on a background thread. You must subclass AsyncTask.
To call the method:
NetworkTask().execute(requestURL, queryString)
Declaration:
private class NetworkTask : AsyncTask<String, Int, Long>() {
override fun doInBackground(vararg parts: String): Long? {
val requestURL = parts.first()
val queryString = parts.last()
// Set up request
val connection: HttpsURLConnection = URL(requestURL).openConnection() as HttpsURLConnection
// Default is GET so you must override this for post
connection.requestMethod = "POST"
// To send a post body, output must be true
connection.doOutput = true
// Create the stream
val outputStream: OutputStream = connection.outputStream
// Create a writer container to pass the output over the stream
val outputWriter = OutputStreamWriter(outputStream)
// Add the string to the writer container
outputWriter.write(queryString)
// Send the data
outputWriter.flush()
// Create an input stream to read the response
val inputStream = BufferedReader(InputStreamReader(connection.inputStream)).use {
// Container for input stream data
val response = StringBuffer()
var inputLine = it.readLine()
// Add each line to the response container
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
// TODO: Add main thread callback to parse response
println(">>>> Response: $response")
}
connection.disconnect()
return 0
}
protected fun onProgressUpdate(vararg progress: Int) {
}
override fun onPostExecute(result: Long?) {
}
}
GET and POST using OkHttp
private const val CONNECT_TIMEOUT = 15L
private const val READ_TIMEOUT = 15L
private const val WRITE_TIMEOUT = 15L
private fun performPostOperation(urlString: String, jsonString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val body = jsonString.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.post(body)
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
private fun performGetOperation(urlString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.get()
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
Object serialization and deserialization
#Throws(JsonProcessingException::class)
fun objectToJson(obj: Any): String {
return ObjectMapper().writeValueAsString(obj)
}
#Throws(IOException::class)
fun jsonToAgentObject(json: String?): MyObject? {
return if (json == null) { null } else {
ObjectMapper().readValue<MyObject>(json, MyObject::class.java)
}
}
Dependencies
Put the following lines in your gradle (app) file. Jackson is optional. You can use it for object serialization and deserialization.
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
You can use this library Fuel Library as well, which makes it further easier.
val map = mutableMapOf<String, String>()
map.put("id","629eeb9da9d8f50016e1af96")
val httpAsync = url
.httpPost()
.jsonBody(
Gson().toJson(map) // for json string
)
.responseString { request, response, result -> //do something with the response }

Categories

Resources