HttpClient, Unirest and a 403 in Kotlin - kotlin

I am trying to understand the following problem when issuing a GET-request to an API in Kotlin.
When sending the request with Unirest ...
val response: com.mashape.unirest.http.HttpResponse<String>? =
Unirest.get(url)
.header(
headerKey,
headerValue
)
.asString()
response?.let {
println("${response.code}")
}
... I get a response code of 200. All fine.
But when I send it with HttpClient ...
val client = HttpClient.newBuilder().build();
val request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.header(
headerKey,
headerValue
)
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString());
println(response.statusCode())
... the response code is 403! Same when I am using Retrofit and OkHttp.
The headerKey is "Authorization" and headerValue is "Bearer ...somekey...".
What am I missing?

Found the issue and it was simple: The user agent was missing in the header.
Therefore I had to add this line to the request-instantiation:
.header("User-Agent", "HttpClient")
Here is the full example:
val client = HttpClient.newBuilder().build()
val request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.header(authHeaderKey, authHeaderValue)
.header("User-Agent", "HttpClient")
.build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString());
println(response.statusCode())

Related

Https request POST issue in Kotlin

I'm new to Kotlin and Android developing.
I'm trying to use the HttpsURL to POST a value of '0' or any other value
to the 'data' variable https://www.mytestsite.com/on-test.php where is a 'data'
variable set inside.
My Kotlin code is reaching the '../on-php' url but the POST method is likely not executed
or no data was transferred for some unknown reason.
I also made another 'post.php' from where I'm posting to the same on-test.php like my Android app,
and that way it is working.
I have no any idea what I'm doing wrong.
Any help I would appreciate.
Here is my code I'm trying to make working:
btn_test.setOnClickListener {
object : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void?): Void? {
val url = "https://www.mytestsite.com/on-test.php"
val data = JSONObject()
data.put("data", "0")
val connection = URL(url).openConnection() as HttpsURLConnection
connection.requestMethod = "POST"
connection.doOutput = true
connection.setRequestProperty("Content-Type", "application/json")
connection.connect()
val wr = DataOutputStream(connection.outputStream)
wr.writeBytes(data.toString())
wr.flush()
wr.close()
error_data = connection.responseMessage
return null
}
}.execute()
text_status.setText(error_data)
}

Ktor how to get http code from request without body

I make a request to the server, but there is no body in the response.
Accordingly, the return value type of response is Unit.
suspend fun foo(
url: String,
id: Long
) {
val requestUrl = "$url/Subscriptions?id=${id}"
val response = httpApiClient.delete<Unit>(requestUrl) {
headers {
append(HttpHeaders.Authorization, createRequestToken(token))
}
}
return response
}
How in this case to receive the code of the executed request?
HttpResponseValidator {
validateResponse { response ->
TODO()
}
}
using a similar construction and throwing an error, for example, is not an option, since one http client is used for several requests, and making a new http client for one request is strange. is there any other way out?
You can specify the HttpResponse type as a type argument instead of Unit to get an object that allows you to access the status property (HTTP status code), headers, to receive the body of a response, etc. Here is an example:
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
suspend fun main() {
val client = HttpClient(Apache)
val response = client.get<HttpResponse>("https://httpbin.org/get")
// the response body isn't received yet
println(response.status)
}

GET request working from Postman but not in Android and the browser

Porblem:
I'm working with an api, and when I'm using postman to do a GET request, works perfect and I get the response as I was expecting. But, when I test the same get request in the browser and in Android Studio, doesn't works.
Postaman snippet:
https://i.stack.imgur.com/b2eeS.png
Postman headers:
Content-Type: application/json
User: Dermacycle2018
Password: (its a large password)
Device: Iphone7
VersionSO: IOS 11.2.2
VersionAPP: 1.0.1
Postman-Token: calculated when request is sent
Host: calculated when request sent
User-Agent: PostmanRuntime/7.28.2
Accept: /
Accept-Encoding: gzip,deflate,br
Connection: keep-alive
Browser snippet:
https://i.stack.imgur.com/6KjpW.png
Android code:
here I am using retrofit to process the response
interface DermaCycleApiClient {
#GET
suspend fun getTratamientosFaciales(#Url url:String)
:Response<TratamientosFacialesResponse>
}
fun getRetrofit():Retrofit{
return Retrofit.Builder().
baseUrl("http://dermaservice.theappmaster.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
suspend fun getTratamientosFaciales(tf:String):TratamientosFacialesResponse{
return withContext(Dispatchers.IO){
val response = retrofit.create(DermaCycleApiClient::class.java).
getTratamientosFaciales("http://dermaservice.theappmaster.com/Tratamiento.ashx?Tipo=TF")
response.body()!!
}
}
I believe your HTTP request require several Headers information as can be seen from your postman data. Seems, there are 5 header values you need to add in your HTTP client header in your Android project
User: Dermacycle2018
Password: (its a large password)
Device: Iphone7
VersionSO: IOS 11.2.2
VersionAPP: 1.0.1
You can add it in your OkHttpClient builder by using an Interceptor.
class HeaderInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val newRequest = chain.request().newBuilder()
.addHeader("User", "Dermacycle2018")
.addHeader("Password", ENCRYPTED_PASSWORD)
.addHeader("Device", DEVICE_NAME)
.addHeader("VersionSO", OS_VERSION)
.addHeader("VersionAPP", "1.0.1")
return chain.proceed(newRequest.build())
}
}
and then add the interceptor in your OkHttpClient builder
val okHttpClient = OkHttpClient().newBuilder()
.addInterceptor(commonHeader)
.build
then add okHttpClient in your retrofit builder
fun getRetrofit(): Retrofit {
val okHttpClient = OkHttpClient().newBuilder()
.addInterceptor(commonHeader)
.build
return Retrofit.Builder()
.baseUrl("http://dermaservice.theappmaster.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
}

Client posting multipart form data

I'm trying to create a client which I use to test my controller
The controller
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
class InjuryController(private val userService: UserService, private val injuryService: InjuryService) {
...
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Post("/injuries/{id}/images")
fun postImage(id: Long, file: CompletedFileUpload, principal: Principal): HttpResponse<*>? {
...
return HttpResponse.ok(imageReference)
}
...
}
The client
#Client("/")
interface InjuryClient {
#Post("/injuries/{id}/images", produces = [MULTIPART_FORM_DATA])
fun postImage(id: Long, body: MultipartBody, #Header authorization: String): ImageReference
}
The test
#Test
fun `Post an image an injury`() {
// Given
val description = "description"
val occurredAt = LocalDateTime.now()
val id = createInjury(description, occurredAt).id
val toWrite = "test file"
val file = File.createTempFile("data", ".txt")
FileWriter(file).apply {
write(toWrite)
close()
}
val requestBody = MultipartBody.builder()
.addPart("data",
file.name,
MediaType.TEXT_PLAIN_TYPE,
file
).build()
// When
val response = injuryClient.postImage(id, requestBody, authorization)
// Then
assertEquals("$id:${file.name}", response.key)
}
The error
The type java.util.LinkedHashMap is not a supported type for a multipart request body
io.micronaut.http.multipart.MultipartException: The type java.util.LinkedHashMap is not a supported type for a multipart request body
at io.micronaut.http.client.DefaultHttpClient.buildMultipartRequest(DefaultHttpClient.java:2063)
at io.micronaut.http.client.DefaultHttpClient.buildNettyRequest(DefaultHttpClient.java:1480)
at io.micronaut.http.client.DefaultHttpClient.sendRequestThroughChannel(DefaultHttpClient.java:1599)
at io.micronaut.http.client.DefaultHttpClient.lambda$null$27(DefaultHttpClient.java:1035)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise(AbstractNioChannel.java:300)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:335)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:688)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)
Any clue about how to get rid of that error?
The client assumes method arguments are members of the body instead of each one representing the entire body. You can achieve the desired behavior by annotating the body argument in the client with #Body

kotlin: retrofit2 getting 404 url not found error

Getting Response{protocol=http/1.1, code=404, message=Not Found, url=https://test.test.com/service/one}
The url is correct as postman works fine.
I have tried looking into this error but most things come back with URL was in correct. and the error itself is vague.
code that starts it. the builder is a json string that is valid. I have tested it in postman.
CoroutineScope(Dispatchers.Default).launch {
val call = submitService.submitCarton(builder.toString())
Log.d("submit", "begining")
withContext(Dispatchers.Main) {
if (call.isSuccessful) {
Log.d("submit",call.body() as String)
} else {
Log.d("submit", "else....")
}
}
}
service factory:
fun makeSubmitService() : SubmitService{
val url = "https://test.test.com/service/"
return Retrofit.Builder().baseUrl(url)
.client(okHttpClient).addConverterFactory(GsonConverterFactory.create())
.build().create(SubmitService::class.java)
}
interface:
interface SubmitService {
#POST("one")
suspend fun submitCarton(#Body json: String): Response<myModel>
}
Expected results are a json response however I am not getting that far.
edit: I created a okhttpclient and did a request manual and I get a message 200 ok.
code for my test
val JSON = MediaType.parse("application/json; charset=utf-8")
val client = OkHttpClient()
val body = "some json"
val requestBody = RequestBody.create(JSON, body)
val request = Request.Builder()
.url("https://test.test.com/service/one")
.post(requestBody)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(request: Request, e: IOException) {
Log.e("test", e.toString())
}
#Throws(IOException::class)
override fun onResponse(response: Response) {
Log.d("test", response.toString())
}
})
Solved it myself.
Issue was dumb, retrofit2 was giving 404 even though the web service was returning a error message.
added
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'
private val interceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
private val okHttpClient = OkHttpClient().newBuilder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.build()
found out retrofit was sending a very unformatted string
"{ \"all my json filled with \" }"
instead of
{ json }
fixed it by adding
.addConverterFactory(ScalarsConverterFactory.create())
to my service factory
for anyone wondering why I am basically creating the json as a string instead of using a JSON object is because the service I talk to really really wants it to be in a very specific order which JSON just don't care about it however it wants it to look like JSON as well...