Currently, I am using Android with Retrofit 2.0.0-beta4 and OkHttp3. I am doing a #PUT request defined as so:
#Headers({
Constants.CONTENT_TYPE_HEADER + ": " + Constants.JSON_HEADER_VAL,
Constants.ACCEPT_HEADER + ": " + Constants.JSON_HEADER_VAL
})
#PUT(Constants.PUT_SKILL_LEVEL)
Call<EmployeeSkill> updateEmpSkillLevel(#Header(Constants.SESSION_COOKIE_NAME) String cookieValue, #Body EmployeeSkillRequest employeeSkillUpdate);
The endpoint goes here: http://apps:8080/employeeSkillsService/employeeSkill. Here is the log output from the PUT request:
D/OkHttp﹕ --> PUT http://apps:8080/employeeSkillsService/employeeSkill http/1.1
D/OkHttp﹕ Content-Type: application/json;charset=UTF-8
D/OkHttp﹕ Content-Length: 99
D/OkHttp﹕ Accept: application/json
D/OkHttp﹕ JSESSIONID: D147377AB60AFE499D2A1AAF7C93F7A3
D/OkHttp﹕ {"employee":{"id":63},"skill":{"isPrimary":false,"isSecondary":false,"id":3},"skillLevel":{"id":5}}
D/OkHttp﹕ --> END PUT (99-byte body)
<-- 404 Not Found http://apps:8080/employeeSkillsService/app.html (23ms)
D/OkHttp﹕ Server: Apache-Coyote/1.1
D/OkHttp﹕ X-Content-Type-Options: nosniff`D/OkHttp﹕ X-XSS-Protection: 1; mode=block`
D/OkHttp﹕ X-XSS-Protection: 1; mode=block
D/OkHttp﹕ Cache-Control: no-cache, no-store, max-age=0, must-revalidate
D/OkHttp﹕ Pragma: no-cache
D/OkHttp﹕ Expires: 0
D/OkHttp﹕ X-Frame-Options: DENY
D/OkHttp﹕ Content-Type: text/html;charset=utf-8
D/OkHttp﹕ Content-Language: en
D/OkHttp﹕ Content-Length: 1029
D/OkHttp﹕ Date: Mon, 07 Mar 2016 15:39:35 GMT
D/OkHttp﹕ OkHttp-Sent-Millis: 1457365206407
D/OkHttp﹕ OkHttp-Received-Millis: 1457365206412
D/OkHttp﹕ <html><head><title>Apache Tomcat/7.0.52 (Ubuntu) - Error report</title><style><!--H1
{font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2
{font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3
{font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B{font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 404 - /employeeSkillsService/app.html</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>/employeeSkillsService/app.html</u></p><p><b>description</b> <u>The requested resource is not available.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.52 (Ubuntu)</h3></body></html>
D/OkHttp﹕ <-- END HTTP (1029-byte body)
I have verified in Postman on Chrome that this endpoint with these headers and payload works multiple times. Yet when I use Retrofit I keep receiving a 404 error despite the endpoint working in Postman. Here is the code call:
Call<EmployeeSkill> updateEmployeeSkillCall = RetrofitApiRestClient.getApiClient().updateEmpSkillLevel(cookieValue, employeeSkillUpdate);
updateEmployeeSkillCall.enqueue(new Callback<EmployeeSkill>() {
#Override
public void onResponse(Call<EmployeeSkill> call, Response<EmployeeSkill> response) {
// called when response HTTP status is "200 OK"
if (response.isSuccess()) {
holder.spnSkillLevel.setTag(pos);
Toast.makeText(mContext, holder.tvSkillName.getText() + mContext.getString(R.string.skill_updated_success_text)
+ holder.spnSkillLevel.getSelectedItem().toString(), Toast.LENGTH_LONG).show();
List<EmployeeSkill> updateEmployeeSkillsRow = EmployeeSkill.find(EmployeeSkill.class, "employee = ? and skill = ?",
response.body().getEmployee().getId().toString(), response.body().getSkill().getId().toString());
EmployeeSkill updatedSkill = updateEmployeeSkillsRow.get(0);
updatedSkill.setSkillLevel(response.body().getSkillLevel());
updatedSkill.setTimeUpdated(response.body().getSkillLevel().getTimeUpdated());
updatedSkill.save();
}
}
#Override
public void onFailure(Call<EmployeeSkill> call, Throwable throwable) {
Toast.makeText(mContext, holder.tvSkillName.getText() + mContext.getString(R.string.skill_update_failed_text), Toast.LENGTH_LONG).show();
}
});
I have tried changing the #Body parameter to a JSON String, but I received an error that stated that the JSON must begin with an array or object. Any help in this matter would be useful. Without this functionality, I will be forced to scrap Retrofit if I cannot find a solution and I am not really looking forward to doing that.
Solved my own issue using Fiddler2. Had the cookie name and value incorrect. Postman was probably more forgiving in how it sent things vs. how Retrofit and others did so. Learned something from it, though. Thanks to all who assisted.
Related
I was trying to add refresh token interceptor to http client
public class AuthInterceptor implements Interceptor {
private Disposable disposable;
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
// if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
//
// synchronized (chain) {
//
//
// RefreshTokenApi refreshTokenApi = new RefreshTokenApi();
// disposable = refreshTokenApi.call().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(
// (tokenResponse, throwable) -> {
//
// PreferencesWrapperImpl.SINGLETON.putStringForKey(tokenResponse.getResponse().header(Constants.AUTH_TOKEN), Constants.PreferenceKey.AUTH_TOKEN_KEY);
// Log.e("throwable", throwable.getMessage());
//
// });
//
//
// Request newRequest = request.newBuilder()
// .removeHeader(Constants.AUTH_TOKEN)
// .addHeader(Constants.AUTH_TOKEN, PreferencesWrapperImpl.SINGLETON.getStringForKey(Constants.PreferenceKey.AUTH_TOKEN_KEY, null))
// .build();
// response = chain.proceed(newRequest);
// }
// }
return response;
}
}
then i discovered that the request proceed many times so i thought that maybe my synchronized logic wrong and that is what caused request to occur multiple time so i commented the synchronized logic but actually the request still occur many times
and here is my logcat
D/OkHttp: --> POST http://134.209.26.2:88/api/common/version/check http/1.1
D/OkHttp:
--> POST http://134.209.26.2:88/api/common/version/check http/1.1
Content-Type: application/json; charset=utf-8
Content-Length: 35
D/OkHttp: Content-Type: application/json; charset=utf-8
Content-Length: 35
D/OkHttp: Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMzQuMjA5LjI2LjI6ODhcL2FwaVwvc3BcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNTg3NjQxMjY5LCJleHAiOjE1ODc2NzcyNjksIm5iZiI6MTU4NzY0MTI2OSwianRpIjoiZThXQUpIenVSamdGOGpORyIsInN1YiI6MTMsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEiLCJwaWQiOjUsInR5cGUiOjF9.sA3-Ga9YI-4AyDZV_KIuOukONu3Abblq6ge8JbaMgp8
x-lang-code: en-us
D/OkHttp: Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMzQuMjA5LjI2LjI6ODhcL2FwaVwvc3BcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNTg3NjQxMjY5LCJleHAiOjE1ODc2NzcyNjksIm5iZiI6MTU4NzY0MTI2OSwianRpIjoiZThXQUpIenVSamdGOGpORyIsInN1YiI6MTMsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEiLCJwaWQiOjUsInR5cGUiOjF9.sA3-Ga9YI-4AyDZV_KIuOukONu3Abblq6ge8JbaMgp8
D/OkHttp: x-user-type: 1
D/OkHttp: x-lang-code: en-us
x-user-type: 1
D/OkHttp: {"user_platform":1,"version":"1.0"}
--> END POST (35-byte body)
D/OkHttp: {"user_platform":1,"version":"1.0"}
--> END POST (35-byte body)
D/OkHttp: <-- 200 OK http://134.209.26.2:88/api/common/version/check (446ms)
D/OkHttp: Date: Sat, 25 Apr 2020 22:01:29 GMT
Server: Apache/2.4.29 (Unix) OpenSSL/1.0.2n PHP/7.2.1 mod_perl/2.0.8-dev Perl/v5.16.3
D/OkHttp: <-- 200 OK http://134.209.26.2:88/api/common/version/check (447ms)
D/OkHttp: Vary: Authorization
D/OkHttp: Date: Sat, 25 Apr 2020 22:01:29 GMT
D/OkHttp: X-Powered-By: PHP/7.2.1
D/OkHttp: Server: Apache/2.4.29 (Unix) OpenSSL/1.0.2n PHP/7.2.1 mod_perl/2.0.8-dev Perl/v5.16.3
D/OkHttp: Cache-Control: no-cache, private
D/OkHttp: Vary: Authorization
D/OkHttp: Content-Length: 46
D/OkHttp: X-Powered-By: PHP/7.2.1
D/OkHttp: Keep-Alive: timeout=5, max=100
D/OkHttp: Cache-Control: no-cache, private
D/OkHttp: Connection: Keep-Alive
D/OkHttp: Content-Length: 46
D/OkHttp: Content-Type: application/json
D/OkHttp: Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json
{"data":{"status":1,"min":null,"latest":null}}
D/OkHttp: {"data":{"status":1,"min":null,"latest":null}}
<-- END HTTP (46-byte body)
D/OkHttp: <-- END HTTP (46-byte body)
E/APP_UPDATES: onSuccess
i use logging interceptor to so as you see the first request of app which is app updates post many times
I am creating new sample app with dagger, Rx, MVP and retrofit2. I am new in retrofit .For loginActivity i have send parameters as json string to web service.If the user exist and has permission, service return true in body and in header send Cookie:
D/Payesh: --> POST http://test.com/Authentication.svc/json/Login
D/Payesh: Content-Type: application/json; charset=utf-8
D/Payesh: Content-Length: 183
D/Payesh: {"username":"****","password":"***","customCredential":"****","isPersistent":false}
D/Payesh: --> END POST (183-byte body)
/data/data/com.groot.payesh/cache/okhttp_cache/journal.tmp
D/Payesh: <-- 200 OK http://test.com/Authentication.svc/json/Login (2111ms)
D/Payesh: Cache-Control: private
D/Payesh: Content-Type: application/json; charset=utf-8
D/Payesh: Server: Microsoft-IIS/8.5
D/Payesh: Set-Cookie: ASP.NET_SessionId=3pi1nyrozbjwagw543Wsck4; path=/
D/Payesh: Set-Cookie: .ASPXAUTH=EA9127C4A3002F1D5EA5AADC279....; expires=Tue, 27-Mar-2018 15:17:22 GMT; path=/
D/Payesh: X-Frame-Options: SAMEORIGION
D/Payesh: Access-Control-Allow-Origin: test.com
D/Payesh: Access-Control-Allow-Headers: Content-Type, Accept
D/Payesh: Access-Control-Allow-Methods: GET, POST
D/Payesh: Access-Control-Max-Age: 1728000
D/Payesh: Access-Control-Allow-Credentials: true
D/Payesh: Date: Mon, 26 Mar 2018 15:17:22 GMT
D/Payesh: Content-Length: 4
D/Payesh: true
D/Payesh: <-- END HTTP (4-byte body)
this is my ApiService:
#POST(UrlManager.AUTHENTICATION+"Login")
Observable<String> getAuthentication(#Body RequestBody params);
this method return a string and i received True when i run my app an get user and pass.
this is my observer method:
public DisposableObserver<String> observer() {
return new DisposableObserver<String>() {
#Override
public void onNext(String responseBody) {
Log.i("--->", "onNext: " + responseBody);
}...
I need this line in header:
Set-Cookie: .ASPXAUTH=EA9127C4A3002F1D5EA5AADC279....; expires=Tue, 27-Mar-2018 15:17:22 GMT; path=/
this is my Interceptor that initialized with dagger from My Application Class:
#AppScope
#Provides
public HttpLoggingInterceptor httpLoggingInterceptor() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
#Override
public void log(String message) {
Timber.d(message);
}
});
return logging.setLevel(HttpLoggingInterceptor.Level.BODY);
}
How can i get Set-Cookie value?
I have searched various SO threads for the same but no results. anyways, I am having trouble calling simple rest api with retrofit. It is a patch api and when I call it from swagger it works fine and response is sent. but when I call from android it doesn't work. I have another similar api request but it works fine with android code as well. please check below details and guide me where I am going wrong.
API Call in Swagger
Request url : http://138.197.110.214:3100/register/resendVerificationCode
Parameters :
{
"email": "ab#gmail.com",
"requestForNewPassword": true,
"sendMail": true
}
Response
{
"connection": "keep-alive",
"date": "Wed, 07 Feb 2018 21:35:48 GMT",
"transfer-encoding": "chunked",
"cache-control": "no-cache",
"content-encoding": "gzip",
"content-type": "application/json; charset=utf-8",
"vary": "accept-encoding"
}
Below is the details for android call
Declaration in interface
#FormUrlEncoded
#PATCH("/register/resendVerificationCode")
public Observable<ResendOTPResponse> doResendForgetOTP(#Field("email") String email, #Field(WebServicesTags.TAG_STR_requestForNewPassword) boolean request, #Field("sendMail") boolean emailbool);
Method call in class
Observable<ResendOTPResponse> register = apiservice.doResendForgetOTP(edtUsername.getText().toString(), true, true); register.subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .onErrorResumeNext(throwable -> {
Toast.makeText(mContext, "" + mContext.getResources().getString(R.string.msg_networkerror), Toast.LENGTH_SHORT).show();
return Observable.empty();
})
.subscribe(new Observer<ResendOTPResponse>() {
#Override
public void onCompleted() {
pdi.dismiss();
}
#Override
public void onError(Throwable e) {
((HttpException) e).code();
}
#Override
public void onNext(ResendOTPResponse resendOTPResponse) {
otpRestricter.setCounter(otpRestricter.getCounter() + 1);
showOTPDialog(mContext, 1);
}
});
Logcat output
D/OkHttp: --> PATCH http://138.197.110.214:3100/register/resendVerificationCode HTTP/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 63
D/OkHttp: email=ahah%40xjxjx.com&requestForNewPassword=true&sendMail=true
D/OkHttp: --> END PATCH (63-byte body)
.
.
.
D/OkHttp: <-- 501 Not Implemented http://138.197.110.214:3100/register/resendVerificationCode (903ms)
D/OkHttp: Content-Type: text/vnd.wap.wml
D/OkHttp: Proxy-Connection: close
D/OkHttp: Connection: close
D/OkHttp: Content-Length: 200
D/OkHttp: OkHttp-Sent-Millis: 1518040494521
D/OkHttp: OkHttp-Received-Millis: 1518040495105
D/OkHttp: <?xml version="1.0"?>
D/OkHttp: <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
D/OkHttp: "http://www.wapforum.org/DTD/wml_1.1.xml">
D/OkHttp: <wml><card id = "error" title = "Error 400">
D/OkHttp: <p>
D/OkHttp: Bad Request
D/OkHttp: </p>
D/OkHttp: </card></wml>
D/OkHttp: <-- END HTTP (200-byte body)
I have a problem with retrofit in android when I try to get the access token it doesn't work at all and I get "unauthorized 401" but using DHC byRESTlet I can get the access token without any problem.
This is the interface I use :
public interface TokenService {
#Headers({
"Content-Type : application/x-www-form-urlencoded",
"Authorization : Basic dGVzdDo="
})
#FormUrlEncoded
#POST("/oauth/token")
Call<TokenEntity> getAccessToken(#Field("username") String username,
#Field("password") String password ,
#Field("grant_type") String grantType );
}
and this is log error :
D/OkHttp: --> POST http://192.168.1.5:8080/oauth/token http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 52
D/OkHttp: Content-Type : application/x-www-form-urlencoded
D/OkHttp: Authorization : Basic dGVzdDo=
D/OkHttp: username=abcd&password=aqwzsx123&grant_type=password
D/OkHttp: --> END POST (52-byte body)
D/OkHttp: <-- 401 Unauthorized http://192.168.1.5:8080/oauth/token (21ms)
D/OkHttp: Server: Apache-Coyote/1.1
D/OkHttp: X-Content-Type-Options: nosniff
D/OkHttp: X-XSS-Protection: 1; mode=block
D/OkHttp: Cache-Control: no-cache, no-store, max-age=0, must-revalidate
D/OkHttp: Pragma: no-cache
D/OkHttp: Expires: 0
D/OkHttp: X-Frame-Options: DENY
D/OkHttp: Cache-Control: no-store
D/OkHttp: Pragma: no-cache
D/OkHttp: WWW-Authenticate: Bearer realm="oauth", error="unauthorized", error_description="Full authentication is required to access this resource"
D/OkHttp: Content-Type: application/json;charset=UTF-8
D/OkHttp: Transfer-Encoding: chunked
D/OkHttp: Date: Fri, 06 May 2016 07:44:03 GMT
D/OkHttp: OkHttp-Sent-Millis: 1462520643079
D/OkHttp: OkHttp-Received-Millis: 1462520643087
D/OkHttp: {"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
<-- END HTTP (102-byte body)
ponse{protocol=http/1.1, code=401, message=Unauthorized, url=http://192.168.1.5:8080/oauth/token}
It does not seem like retrofit problem.
You may try using #Body instead of fields, try making class:
class Request {
String username;
...
}
and then
Call<TokenEntity> getAccessToken(#Body Request request);
What the title says. The problem only happens when using Retrofit. Uploads using Postman or Android's HTTPURLConnection+DataOutputStream work fine and return the json body from the REST api.
Here's a working request/response log from Postman:
Remote Address: xxx.xxx.xxx.xxx:80 Request URL: [ --- secret --- ]
Request Method:POST Status Code:200 OK Response Headers (8) Request
Headers view source Accept:*/* Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8,pt;q=0.6,gl;q=0.4
Cache-Control:no-cache Connection:keep-alive Content-Length:11519
Content-Type:multipart/form-data;
boundary=----WebKitFormBoundaryGlChAAe9WOKlPy2D
Cookie:laravel_session= [...] Host: [secret]
Request Payload
------WebKitFormBoundaryGlChAAe9WOKlPy2D Content-Disposition: form-data; name="upload[]"; filename="avatar.png" Content-Type:
image/png
------WebKitFormBoundaryGlChAAe9WOKlPy2D--
Here's the Retrofit version:
----> HTTP POST http://xxxxxxx
Accept: application/json
Content-Type: multipart/form-data
Content-Length: 9094
--1d87e0b7-bf5d-4034-b20b-48155a5dee2a
Content-Disposition: form-data; name="upload[]" filename="picSuggestion.jpg"
Content-Type: image/jpeg Content-Length: 8846
Content-Transfer-Encoding: binary
---> END HTTP (9094-byte body)
<--- HTTP 200 http://xxxxxxxx/api/uploads/?api=true&token=xxxx(1240ms)
: HTTP/1.1 200 OK
Cache-Control: no-cache
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Date: Fri, 28 Aug 2015 13:11:20 GMT
Server: nginx/1.4.6 (Ubuntu)
Set-Cookie: laravel_session=xxxxxxx; path=/; httponly
Transfer-Encoding: chunked
X-Android-Received-Millis: 1440767479264
X-Android-Response-Source: NETWORK 200
X-Android-Sent-Millis: 1440767478578
X-Powered-By: PHP/5.5.9-1ubuntu4.11
<--- END HTTP (0-byte body)
We tried a bunch of Retrofit service and interceptor configurations without success, but here's the latest one we tried
requestInterceptor2 = new RequestInterceptor() {
#Override
public void intercept(RequestInterceptor.RequestFacade request) {
request.addHeader("Content-Type", "multipart/form-data" );
request.addHeader("Accept", "application/json" );
request.addQueryParam("key", API_KEY);
}
};
#Multipart
#POST("/api/uploads/")
void uploadImage(#Part(value = "upload[]", encoding = "binary") TypedFile payload, #Query("api") boolean api, #Query("token") String token, Callback<uploadImagePoiResultVO> cb);