I am using retrofit to pass login and register api in android. But I am getting response as 409 in return. I am not getting data from api. Retrofit 2 is used here
SignUpApi signupapi = Api_Config.getInstance3().getApiBuilder().create(SignUpApi.class);
Call<SignUpApi.ResponseSignUp> call = signupapi.POSTDATA(UserName.getText().toString().trim(),
Email.getText().toString().trim(),
Password.getText().toString().trim(),
Sex.getText().toString().trim(),
Mobile.getText().toString().trim());
call.enqueue(new Callback<SignUpApi.ResponseSignUp>() {
#Override
public void onResponse(Call<SignUpApi.ResponseSignUp> call, Response<SignUpApi.ResponseSignUp> response) {
CustomProgressDialog.getInstance().dismiss();
if (response.isSuccessful()){
Log.e("Status is",response.body().getStatus().toString());
if (response.body().getStatus() == 200){
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,Constants.SuccessfullyRegistered);
CommonFunctions.getInstance().FinishActivityWithDelay(SignInActivity.this);
}else if (response.body().getStatus() == 409){
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,Constants.YouAreAlreadyRegistered);
}else{
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,response.body().getMsg());
}
} else {
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,Constants.SomethingWentWrong);
}
}
#Override
public void onFailure(Call<SignUpApi.ResponseSignUp> call, Throwable t) {
CustomProgressDialog.getInstance().dismiss();
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,t.getMessage());
}
});
}
Below is my API configuration
public static Api_Config getInstance3()
{
if (ourInstance == null){
synchronized (Api_Config.class){
if ( ourInstance == null )
ourInstance = new Api_Config();
}
}
ourInstance.config3();
return ourInstance;
}
private void config3() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
String BASE_URL3 = LOGIN_AND_SIGNUP;
mRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL3)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
Below is my Api Class
public interface SignUpApi {
#FormUrlEncoded
#POST("register.php")
Call<ResponseSignUp> POSTDATA(#Field("user_name")String username,
#Field("user_email")String email,
#Field("user_password")String password,
#Field("user_gender")String sex,
#Field("user_mobile")String mobile
);
public class ResponseSignUp
{
#SerializedName("status")
#Expose
private Integer status;
#SerializedName("msg")
#Expose
private String msg;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
I am newbee to android and really confused why my code is not working. Looking for help. Thanks in Advance
Note that if your response was successfully it means you got a successful code (200...300). However, if you get a response 401, 409.. it means you got an error, then your response was not successfully. Put the error handle outside the response.isSuccessful() condition.
if (response.isSuccessful()){
//Handle success response here
Log.e("Status is",response.body().getStatus().toString());
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,Constants.SuccessfullyRegistered);
CommonFunctions.getInstance().FinishActivityWithDelay(SignInActivity.this);
} else {
// Handle error response here, 401, 409...
if (response.body().getStatus() == 409){
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,Constants.YouAreAlreadyRegistered);
}else{
CommonFunctions.getInstance().ShowSnackBar(SignInActivity.this,response.body().getMsg());
}
}
You can check this in the retrofit2 files.
/** Returns true if {#link #code()} is in the range [200..300). */
public boolean isSuccessful() {
return rawResponse.isSuccessful();
}
EDIT
Depper explanation
I'll try to explain better. When you call retrofit using enqueue method like this: call.enqueue() you expect to get a Response or Failure from the server: onResponse() means you got a response and onFailure() means you failed to connect to the server, it could mean the server is broken or there is no internet connection.
If you got a onResponse() from the server it does not mean it was successful, it just means you got a response, therefore you need to check if this response was successful or not by using this condition
if (response.isSuccessful){
}
What is a successful response?
If you end up inside this condition response.isSuccessful it already means you got a successful response and this is a response with code between 200 and 300.
However, if you want to check if you got a 409 code. 409 code means that it was a unsuccessful response, then you need to check this outside the success condition.
if (response.isSuccessful){
// You got a successful response, the code is from 200 to 300.
} else {
// You got a unsuccessful response, handle the code 401, 405, 409 here.
}
Related
I've tried making a retrofit call to an API endpoint, but it's returning a 400 error, however my curl request is working perfectly fine. I can't seem to spot the error, could someone double check my work to see where I made a mistake?
The curl call that works:
curl --request POST https://connect.squareupsandbox.com/v2/payments \
--header "Content-Type: application/json" \
--header "Authorization: Bearer accesstoken112233" \
--header "Accept: application/json" \
--data '{
"idempotency_key": "ab2a118d-53e2-47c6-88e2-8c48cb09bf9b",
"amount_money": {
"amount": 100,
"currency": "USD"},
"source_id": "cnon:CBASEITjGLBON1y5od2lsdxSPxQ"}'
My Retrofit call:
public interface IMakePayment {
#Headers({
"Accept: application/json",
"Content-Type: application/json",
"Authorization: Bearer accesstoken112233"
})
#POST(".")
Call<Void> listRepos(#Body DataDto dataDto);
}
DataDto class:
public class DataDto {
private String idempotency_key;
private String amount_money;
private String source_id;
public DataDto(String idempotency_key, String amount_money, String source_id) {
this.idempotency_key = idempotency_key;
this.amount_money = amount_money;
this.source_id = source_id;
}
}
And lastly making the retrofit call:
DataDto dataDto = new DataDto("ab2a118d-53e2-47c6-88e2-8c48cb09bf9b", "{\"amount\": 100, \"currency\": \"USD\"}", "cnon:CBASEITjGLBON1y5od2lsdxSPxQ");
RetrofitInterfaces.IMakePayment service = RetrofitClientInstance.getRetrofitInstance().create(RetrofitInterfaces.IMakePayment.class);
Call<Void> call = service.listRepos(dataDto);
call.enqueue(new Callback<Void>() {
#Override
public void onResponse(#NonNull Call<Void> call, #NonNull Response<Void> response) {
Log.d(TAG, "onResponse: " + response.toString());
}
#Override
public void onFailure(#NonNull Call<Void> call, #NonNull Throwable t) {
Log.d(TAG, "onFailure: Error: " + t);
}
});
Retrofit Instance:
public class RetrofitClientInstance {
private static Retrofit retrofit;
private static final String BASE_URL = "https://connect.squareupsandbox.com/v2/payments/";
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Edit 1: Changing to second parameter to JSON Object
JSONObject jsonObject = new JSONObject();
try{
jsonObject.put("amount", 100);
jsonObject.put("currency", "USD");
}catch (Exception e){
Log.d(TAG, "onCreate: " + e);
}
DataDto dataDto = new DataDto("ab2a118d-53e2-47c6-88e2-8c48cb09bf9b", jsonObject, "cnon:CBASEITjGLBON1y5od2lsdxSPxQ");
First of all, let's see what 400 means
The HyperText Transfer Protocol (HTTP) 400 Bad Request response status
code indicates that the server cannot or will not process the request
due to something that is perceived to be a client error (e.g.,
malformed request syntax, invalid request message framing, or
deceptive request routing).
Now we are sure, the problem stands in our request (not server fault), most probably it is because you are trying to convert JSON in request (do not do this explicitly GSON will convert automatically)
Use interceptor to verify your outgoing network requests (Tell the result here)
you use #POST(".") which does not make sense, please understand BASE_URL is your server URL NOT MORE
The problem could be translating this post request
So a possible solution
Change base URL into "https://connect.squareupsandbox.com/"
Replace #POST(".") with #POST("v2/payments/")
PS. #NaveenNiraula mentioned right thing even though it did not help you, please follow his instruction, it is the correct way parsing data using GSON (make sure you include it and configure it correctly) converter
EDIT
I make it work (I eliminated 400 error code that is what you want as long as question title is concerned) partially which means I detect why 400 error was occurred and fixed it but unfortunately, I stuck the UNAUTHORIZED issue. The problem was relating to converting json and data type
data class DataDTO(
val idempotency_key: String,
val source_id: String,
val amount_money: MoneyAmount
)
data class MoneyAmount(
val amount: Int,
val currency: String
)
I gist all code here you can refer
You need two DTO classes as below:
public class Amount_money
{
private String amount;
private String currency;
public String getAmount ()
{
return amount;
}
public void setAmount (String amount)
{
this.amount = amount;
}
public String getCurrency ()
{
return currency;
}
public void setCurrency (String currency)
{
this.currency = currency;
}
#Override
public String toString()
{
return "ClassPojo [amount = "+amount+", currency = "+currency+"]";
}
}
And
public class DataDto
{
private String idempotency_key;
private Amount_money amount_money;
private String source_id;
public String getIdempotency_key ()
{
return idempotency_key;
}
public void setIdempotency_key (String idempotency_key)
{
this.idempotency_key = idempotency_key;
}
public Amount_money getAmount_money ()
{
return amount_money;
}
public void setAmount_money (Amount_money amount_money)
{
this.amount_money = amount_money;
}
public String getSource_id ()
{
return source_id;
}
public void setSource_id (String source_id)
{
this.source_id = source_id;
}
#Override
public String toString()
{
return "ClassPojo [idempotency_key = "+idempotency_key+", amount_money = "+amount_money+", source_id = "+source_id+"]";
}
}
You need to create object for each like under :
Amount_money am = new Amount_money();
am.setAmount("100");
am.setCurrency("USD");
DataDto dto = new DataDto();
dto.setIdempotency_key("your key");
dto.setsource_id("your id");
dto.setAmount_money(am);
RetrofitInterfaces.IMakePayment service = RetrofitClientInstance.getRetrofitInstance().create(RetrofitInterfaces.IMakePayment.class);
Call<Void> call = service.listRepos(dataDto);
// yo get the point follow along
Most likely the passed JSON structure is not serialized in the same format.
"amount_money": {
"amount": 100,
"currency": "USD"},
I would at first use for private String amount_money; a real DTO having the amount and currency fields. This should give progress. I'm not 100% sure how the underscore mapping of attributes looks like, but this is the next step.
Add logging to be able to see the passed data. A quick search reveals this tutorial: https://futurestud.io/tutorials/retrofit-2-log-requests-and-responses. When seeing the transmitted data it should be easy to compare the expected and sent data.
Please check your base url.
In your curl you have https://connect.squareupsandbox.com/v2/payments
But in the code you have
private static final String BASE_URL = "https://connect.squareupsandbox.com/v2/payments/";
There is extra / (slash) in the end. I've seen cases where it was the issue. Could be your problem :)
How can I manage server response if status is either than 200.
#JsonSerializable(nullable: false)
class LoginResponse {
final String error;
final int status;
final List<User> userList;
LoginResponse({this.error, this.status, this.userList});
factory LoginResponse.fromJson(Map repJson){
List<dynamic> userListResp=repJson['userData'];
List<User> userList=userListResp.map((e)=>User.fromUser(e)).toList();
int s=repJson['status'];
if(s==200){
return LoginResponse(error:repJson['error'],status: repJson['status'],userList:userList);
} else{
return LoginResponse(error:repJson['error'],status: repJson['status']);
}}}
class User{
String cust_id;
String cust_name;
String cust_email;
String cust_mob;
User({this.cust_id,this.cust_name,this.cust_email,this.cust_mob});
factory User.fromUser(Map userJson){
return User(cust_id: userJson['cust_id'],cust_name: userJson['cust_name'],
cust_email: userJson['cust_email'],cust_mob: userJson['cust_mob']);
}
}
server response when an error is occur
{"error":"1","status":201,"message":"Entered email id already exist in our records"}
server response on success
{
"error":"0",
"status":200,
"userData":[
{
"cust_id":"87",
"cust_name":"kio",
"cust_email":"kio1#kio.com",
"cust_gend":null,
"cust_dob":null,
"cust_mob":"098998899889588",
"cust_pass":"e10adc3949ba59abbe56e057f20f883e",
"cust_age":null,
"device_type":"android",
"device_token":"eNWqzDwxqsQ:APA91bF-uK1MI11D3SgHGSw7Omv1imjDrPKBBCrN9JgmyJppHsNVeG5l56EkCCd5ZMaxL_ehQzVhtoEj0fTNB55wYGJt5BqYVvwfAb7HrBqwb_21M6VFPuF6LQINkvE1offQgZYweROO",
"status":"0",
"createAt":"2019-01-31 18:45:19",
"updatedAt":"0000-00-00 00:00:00",
"login_type":"",
"login_id":null,
"is_guest":"0",
"auth_token":"",
"forgot_token":null
}]
}
How can I manage when user data is not present or null, I tried to manage when the status code is 201 but still showing
NoSuchMethodError: The method 'map' was called on null.
To fix your code move the userList mapping inside the if block. This way you will parse the response only of the status code is 200.
int s=repJson['status'];
if (s==200) {
List<dynamic> userListResp=repJson['userData'];
List<User> userList=userListResp.map((e)=>User.fromUser(e)).toList();
return LoginResponse(error:repJson['error'], status:repJson['status'], userList:userList);
} else {
return LoginResponse(error:repJson['error'], status:repJson['status']);
}
However, you might not want to handle errors in your model. It is better to check for error after you performed the request and then decide if you want to parse the response.
Something like this will be easier to handle and won't pollute your model object:
final response = await client.get(requestUrl);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
final loginResponse = LoginResponse.fromJson(json.decode(response.body));
// ...
} else {
// If that call was not successful, throw an error or parse the error object.
throw Exception('Failed to login');
// ...
}
I am developing simple Android app where I am using google spreadsheet as a data source. For communication I am using google app script which implements doPost method because my app is sending some data to sheet and also wants some data as a response. The problem is instead of json response I always get html response about redirection in the errorBody().
I have also set OkHttpClient with redirections enabled to my retrofit service, but result is still the same.
I am working with Insomnia rest client for debugging and when I set redirections on there, everything works there fine.
If somebody had the same problem and solved it, please help.
Edit:
Here is my code:
public class Connector {
private static final String BASE_URL = "https://script.googleusercontent.com/";
private static final Object LOCK = new Object();
private static CallTaxiService service;
private static final String TAG = "Connector";
private static CallTaxiService getService()
{
if (service == null)
{
synchronized(LOCK) {
Log.d(TAG, "creating instance");
service = buildService();
}
}
return service;
}
private static CallTaxiService buildService()
{
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(new OkHttpClient.Builder().followRedirects(true)
.followSslRedirects(true).build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(CallTaxiService.class);
}
public static void syncData(List<TaxiServiceAppData> data, Callback<Response> callback)
{
Call<Response> call = getService().sendData(data);
Log.d(TAG, "syncing data");
call.enqueue(callback);
}
private interface CallTaxiService {
#Headers({"Content-type: application/json"})
#POST("endpoint_url")
Call<Response> sendData(#Body List<TaxiServiceAppData> data);
}
}
And here is how I am calling it:
Connector.syncData(taxiServiceAppData, new retrofit2.Callback<com.adrisoft.calltaxi.model.Response>() {
#Override
public void onResponse(Call<com.adrisoft.calltaxi.model.Response> call, Response<com.adrisoft.calltaxi.model.Response> response) {
com.adrisoft.calltaxi.model.Response data = response.body();
if (data != null) {
newCities = data.getCities();
newTaxis = data.getTaxis();
updateDb();
prefs.saveSyncTime();
isSyncRunning = false;
callback.onSuccess();
} else {
try {
Log.d(TAG, "Sync failed ... no data available. Error: " + response.errorBody().string());
} catch (Exception ex) {
}
callback.onFailure();
}
}
#Override
public void onFailure(Call<com.adrisoft.calltaxi.model.Response> call, Throwable t) {
Log.d(TAG, "Sync request failed.");
isSyncRunning = false;
callback.onFailure();
}
});
And exactly in the log "Sync failed ... no data available ..." I am getting this from errorBody():
<HTML>
<HEAD>
<TITLE>Temporary Redirect</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Temporary Redirect</H1>
The document has moved here.
</BODY>
</HTML>
Redirect could have happened because the server endpoint provided https and in your code you call http. Then the server would redirect to https. Only GET requests can be redirected, so others like POST will result in error.
I am using Retrofit 2 in my Android project. When I hit an API endpoint using a GET method and it returns a 400 level error I can see the error content when I use an HttpLoggingInterceptor, but when I get to the Retrofit OnResponse callback the error body's string is empty.
I can see that there is a body to the error, but I can't seem to pull that body when in the context of the Retrofit callback. Is there a way to ensure the body is accessible there?
Thanks,
Adam
Edit:
The response I see from the server is:
{"error":{"errorMessage":"For input string: \"000001280_713870281\"","httpStatus":400}}
I am trying to pull that response from the response via:
BaseResponse baseResponse = GsonHelper.getObject(BaseResponse.class, response.errorBody().string());
if (baseResponse != null && !TextUtils.isEmpty(baseResponse.getErrorMessage()))
error = baseResponse.getErrorMessage();
(GsonHelper is just a helper which passes the JSON string through GSON to pull the object of type BaseResponse)
The call to response.errorBody().string() results in an IOException: Content-Length and stream length disagree, but I see the content literally 2 lines above in Log Cat
I encountered the same problem before and I fixed it by using the code response.errorBody().string() only once. You'll receive the IOException if you are using it many times so it is advised to just use it as a one-shot stream just as the Documentation on ResponseBody says.
My suggestion is: convert the Stringified errorBody() into an Object immediately as the latter is what you're gonna be using on subsequent operations.
As it was mentioned, you need to use response.errorBody().string() only once. But there is a way to get the error body string more than once.
TL;DR Use the code below to get error body string from response more than once.
public static String getErrorBodyString(Response<?> response) throws IOException {
// Get a copy of error body's BufferedSource.
BufferedSource peekSource = response.errorBody().source().peek();
// Get the Charset, as in the original response().errorBody().string() implementation
MediaType contentType = response.errorBody().contentType(); //
Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;
charset = Util.bomAwareCharset(peekSource, charset);
// Read string without consuming data from the original BufferedSource.
return peekSource.readString(charset);
}
Explanation:
This is based on the original response.errorBody().string() method implementation. It uses the copy of BufferedSource from peek() and returns the error body string without consuming it, so you can call it as many times as you need.
If you look at the response.errorBody().string() method implementation, you'll see this:
public final String string() throws IOException {
try (BufferedSource source = source()) {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
}
}
source.readString(charset) consumes data of the error body's BufferedSource instance, that's why response.errorBody().string() returns an empty string on next calls.
To read from error body's BufferedSource without consuming it we can use peek(), which basically returns a copy of the original BufferedSource:
Returns a new BufferedSource that can read data from this
BufferedSource without consuming it.
you can use Gson to get errorBody as your desired model class:
val errorResponse: ErrorMessage? = Gson().fromJson(
response.errorBody()!!.charStream(),
object : TypeToken<ErrorMessage>() {}.type
)
First create an Error class like below:
public class ApiError {
#SerializedName("httpStatus")
private int statusCode;
#SerializedName("errorMessage")
private String message;
public ApiError() {
}
public ApiError(String message) {
this.message = message;
}
public ApiError(int statusCode, String message) {
this.statusCode = statusCode;
this.message = message;
}
public int status() {
return statusCode;
}
public String message() {
return message;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
}
Second you can create a Utils class to handle your error like below:
public final class ErrorUtils {
private ErrorUtils() {
}
public static ApiError parseApiError(Response<?> response) {
final Converter<ResponseBody, ApiError> converter =
YourApiProvider.getInstance().getRetrofit()
.responseBodyConverter(ApiError.class, new Annotation[0]);
ApiError error;
try {
error = converter.convert(response.errorBody());
} catch (IOException e) {
error = new ApiError(0, "Unknown error"
}
return error;
}
And finally handle your error like below:
if (response.isSuccessful()) {
// Your response is successfull
callback.onSuccess();
}
else {
callback.onFail(ErrorUtils.parseApiError(response));
}
I hope this'll help you. Good luck.
If you are gettig 400 then its a bad request you r trying to send to server.
check your get req.
I'm trying to get a json list from a web service.
This is the json string return by server :
[{"categoryName":"Política"},{"categoryName":"Economía"},{"categoryName":"Cultura"},{"categoryName":"Deportes"}
The problem is converting in to the POJO. The special characters (í) it's appear like "Pol�tica".
This is the retrofit call function :
#GET("categories")
public Call<List<CategoryPojo>> getCategorias(#Query("sitename") String site)
this is the callback function:
Call<List<CategoryPojo>> call = restservice.getApiService().getCategorias(medio);
try {
call.enqueue(new Callback<List<CategoryPojo>>() {
#Override
public void onResponse(Call<List<CategoryPojo>> call, Response<List<CategoryPojo>> response) {
List<CategoryPojo> categories = response.body();
if (listener != null)
listener.onDataLoaded(categories);
}
#Override
public void onFailure(Call<List<CategoryPojo>> call, Throwable throwable) {
Log.e("Retrofit Error", throwable.getMessage());
}
});
this is the POJO:
public class CategoryPojo implements Serializable{
public CategoryPojo() { }
#SerializedName("categoryName")
private String name;
public String getName()
{
return this.name;
}
}
The result of the request to the Web services, (output in browser) is :
[{"categoryName":"Política"},{"categoryName":"Economía"},{"categoryName":"Cultura"},{"categoryName":"Deportes"},{"categoryName":"Salud"},{"categoryName":"Ciencia y Tecnología"},{"categoryName":"Medio Ambiente"},{"categoryName":"Medios"},{"categoryName":"Militar e Inteligencia"},{"categoryName":"Sociedad"}]
So, the return json has a good encoding...i think that maybe is about the way retrofit read the response.
I'm using retrofit-2.0.2, gson-2.6.1, converter-gson-2.0.2, okhttp-3.2.0.
Any help? please
You should check Content-type in the response headers. Look for the charset value and try to change that on the backend side to application/josn;charset=UTF-8. That worked for me.