I need to execute post request with retrofit but i have a problem which i can't understand very well. Before trying with code i tested api call with Postman and request look like this:
Here is my android code:
public class API {
private static <T> T builder(Class<T> endpoint) {
return new Retrofit.Builder()
.baseUrl(Utils.API_BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(endpoint);
}
public static AllRequests request() {
return builder(AllRequests.class);
}
}
EDIT request:
#POST("api/android-feedback")
#Headers({"Content-Type: application/x-www-form-urlencoded", "Authorization: F##3FA##Rad!#%!2s"})
Call<String> sendFeedback(#Body FeedbackBody body);
FeedbackBody:
public class FeedbackBody{
private final String email;
private final String feedback;
public FeedbackBody(String email, String feedback){
this.email = email;
this.feedback = feedback;
}
}
And finally i construct the request and wait for response, the problem is that i receive message in onFail method
private void sendFeedbackRequest(){
API.request().sendFeedback(new FeedbackBody("testmeil#meil.com", "test feedback").enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
goToMainActivity();
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(SplashScreenActivity.this, R.string.try_again_later, Toast.LENGTH_SHORT).show();
}
});
EDIT:
Still not working.. i think i figure it out where can be the problem, because server side wait for simple POST request without Json formatting, i think Retrofit use JSON formatting by default, and if i send POST request and format Body parameters with JSON the server will fail to parse my request, is there any chance to send simple POST request like at POSTMAN without formatting with JSON ?
Php api wait request to be send like this:
$_POST['feedback'] = 'blabla';
$_POST['email'] = 'blabla..';
and if he receive Json format request can't parse it and because of that i receive fail response.
First you need to create request( POJO Class)
public class FeedbackRequest {
public String email;
public String feedback;
}
when you call sendFeedbackRequest() pass the FeedbackRequest like below"
FeedbackRequest req = new FeedbackRequest();
req.email= "email";
req.feedback= "feedback"
sendFeedbackRequest(req)
after that your sendFeedbackRequest() should be like this
private void sendFeedbackRequest(FeedbackRequest request){
API.request().sendFeedback(request).enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
goToMainActivity();
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(SplashScreenActivity.this, R.string.try_again_later, Toast.LENGTH_SHORT).show();
}
});
And your retrofit request should be like this,
#FormUrlEncoded
#POST("api/android-feedback")
#Headers({"Content-Type: application/json", "Authorization: F31daaw313415"})
Call<String> sendFeedback(#Body FeedbackRequest request);
Now it should work. feel free to ask anything.
You are using a Gson converter factory. It might be easier to create a single object that represents your body, and use that instead of all individual parameters. That way, you should be able to simple follow along with the examples on the Retrofit website.enter link description here
There are also many site that let you generate your Plain Old Java Objects for you, like this one:
E.g. your Api call:
#POST("api/android-feedback")
Call<String> sendFeedback(#Body FeedbackBody feedback);
And your FeedbackBody class:
public class FeedbackBody{
private final String email;
private final String feedback;
public FeedbackBody(String email, String feedback){
this.email = email;
this.feedback = feedback;
}
}
Java:
#POST("/api/android-feedback")
Call<String> sendFeedback(#Body FeedbackBody feedback);
Kotlin:
#POST("/api/android-feedback")
fun sendFeedback(#Body feedback: FeedbackBody): Call<String>
Also, probably you forgot leading slash in the endpoint.
val formBody: RequestBody = FormBody.Builder()
.add("username", LoginRequest.username)
.add("password", LoginRequest.password)
.add("grant_type",LoginRequest.grant_type)
.add("client_id", LoginRequest.client_id)
.add("client_secret", LoginRequest.client_secret)
.add("cleartext", LoginRequest.cleartext)
.build()
#POST(EndPoints.GENERATE_TOKEN_URL)
#Headers("Content-Type: application/x-www-form-urlencoded")
suspend fun getLogin(
#Body formBody: RequestBody
): LoginResponse
Related
So I have this POST request made to the server and based on an argument the server will return error message within the errorBody() of Retrofit. I am trying to handle that Plain Text error returned by the server and then display it to the user within my Android application which uses Java. Below is my current attempt but this is giving me this error in Logcat:
#Url cannot be used with #POST URL (parameter #1)
Here is 400 response from the server:
Interface:
public interface ChangePickLocationClient
{
#GET
Call<ResponseBody> checkItem(#Url String url, #Header("Authorization") String authToken);
#GET
Call<String> getStringError(#Url String url, #Header("Authorization") String authToken);
#POST("Pick/ChangePickLocationAcceptChange")
Call<String> changePickLocationPOST(#Url String url, #Header("Authorization") String authToken, #Body
ChangePickLocationPostModel changePickLocationPostModel);
}
Implementation:
private static final String BASE_URL = "http://00.00.00.1234/api/";
Gson mGson = new Gson();
Retrofit retrofit = new Retrofit.Builder().client(new OkHttpClient())
.baseUrl(BASE_URL).addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(mGson))
.build();
ChangePickLocationClient ChangePickLocationClient =
retrofitPOST.create(ChangePickLocationClient.class);
String itemNumber = itemNumberValue.getText().toString();
newPickLocationValue.setText(newPickLocationValue.getText().toString().toUpperCase());
String newPickLocation = newPickLocationValue.getText().toString();
String token = globalClass.getActiveToken();
final ChangePickLocationClient mChangePickLocationInterface =
retrofit.create(ChangePickLocationClient.class);
Call<String> mCallErrorPOST = mChangePickLocationInterface.changePickLocationPOST
(postUrl, "Bearer " + globalClass.getActiveToken(),
changePickLocationPostModel);
call.enqueue(new Callback<ChangePickLocationPostModel>()
{
#Override
public void onResponse(Call<ChangePickLocationPostModel> call,
Response<ChangePickLocationPostModel> response)
{
String mPlainTextResponse = null;
try {
if(response.errorBody() != null)
{
mPlainTextResponse = response.errorBody().string();
}
} catch (IOException e)
{
e.printStackTrace();
}
Toast.makeText(ChangePickLocation.this, mPlainTextResponse
,Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<ChangePickLocationPostModel> call, Throwable t)
{
Toast.makeText(ChangePickLocation.this, "Unknown server error!"
,Toast.LENGTH_SHORT).show();
}
});
When the response is 400, the second call being made needs to be a clone() call. This is because the Call cannot be used more than once as stated in the documentation.
use this:
call.clone().enqueue(new Callback<ChangePickLocationPostModel>()
instead of
call.enqueue(new Callback<ChangePickLocationPostModel>()
I know there are a lot of threads regarding this and i did go through them and also looked into the same Retrofit POST example,but i'm not sure what am i doing wrong in this,lemme know if any
#Multipart
#POST("api/customerdetail")
Call<Array> addUser(#Part("CustomerName") String CustomerName, #Part("CustomerId") String CustomerId, #Part("UserId") String UserId, #Part("VehicleCompanyName") String VehicleCompanyName, #Part("VehicleModelType")String VehicleModelType, #Part("VehicleNumber")String VehicleNumber, #Part("Location")String Location);
//METHOD USED TO CALL
private void simpleMethod() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://endpoint.net/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GetDataService service = retrofit.create(GetDataService.class);
Call<Array> arrayListCall = service.addUser("Peter Jenkins", UUID.randomUUID().toString(),"user2","AUDI","R3","BVZ-009","-55,-93.23"); arrayListCall.enqueue(new Callback<Array>() {
#Override
public void onResponse(Call<Array> call, Response<Array> response) {
Log.e("RESPONSE",response.toString());
}
#Override
public void onFailure(Call<Array> call, Throwable t) {
Log.e("ERROR",t.toString());
} }); }
Works like a Charm in Postman,and uploading an image is not necessary,atleast from the api end
Any inputs would be deeply appreciated
I would really really appreciate any help I can get on this! Sorry for the long question.
I'm creating this android app, where to sign up, users will type in their phone number and submit it, to get a verification code via text message.
I have worked off of this tutorial:
https://code.tutsplus.com/tutorials/sending-data-with-retrofit-2-http-client-for-android--cms-27845
I have reduced the 2 fields in their app to one field - one text field for a phone number, and a submit button below. This phone number is to be sent to the API.
I'm really new to Retrofit, and I've been trying for a while now to successfully send a call to the API. I have tested the API call by using the 'Postman' desktop app, and the API is alive and responding...I've just not been able to form a valid request to send to the API.
The JSON schema our API guy designed...for this activity needs just one string, the phone number:
{
"phone_number": "string"
}
and then if it is a valid phone number and the user isn't in the database, you get back a 200 response
{
"message": "string"
}
OR you can get back a 400 response from the API
{
"error": "string",
"description": "string"
}
My retrofit interface, called APIService.java looks like this:
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.Body;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
public interface APIService {
#POST("/verifications/signup/send")
#FormUrlEncoded
Call<Post> sendPhoneNumber(#Field("phone_number") String phone_number);
}
I am really new to retrofit2, and above, I can sense one issue, which I don't know how to solve. From the API schema I was given, this one parameter I sent to the API should be 'body'....not 'field'. Maybe in retrofit #Body...I am not too sure how to implement that in this java file above.
Now, what I did below might be really stupid...I don't understand how retrofit java 'model' classes should be made. I followed one tutorial that modeled the class after the RESPONSE, rather than the data call. So, I modified their Post class (which is what I called my ?JSON object to send a single phone number). So my Post class looks like this:
public class Post {
#SerializedName("message")
#Expose
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
#Override
public String toString() {
//return "Post{" + "message = " + message + '}';
return "This is a return message string";
}
}
Honestly, I think what I've done might be totally wrong, but I am not sure how to design the object(Post) class, considering I don't even know what this class will be used for...except getting the response back?
public class MainActivity extends Activity {
private TextView mResponseTv;
private APIService mAPIService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText phoneNumberEt = (EditText)
findViewById(R.id.et_phoneNumber);
Button submitBtn = (Button) findViewById(R.id.btn_submit);
mResponseTv = (TextView) findViewById(R.id.tv_response);
mAPIService = ApiUtils.getAPIService();
submitBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String phoneNumber = phoneNumberEt.getText().toString().trim();
if (!TextUtils.isEmpty(phoneNumber)) {
sendPost(phoneNumber);
}
}
});
}
public void sendPost(String phone_number) {
mAPIService.sendPhoneNumber(phone_number).enqueue(new Callback<Post>() {
#Override
public void onResponse(Call<Post> call, Response<Post> response) {
int statusCode = response.code();
if (response.isSuccessful()) {
showResponse("Response code is " + statusCode + ". Submitted successfully to API - " + response.body().toString());
Log.i(TAG, "post submitted to API." + response.body().toString());
}
}
#Override
public void onFailure(Call<Post> call, Throwable t) {
showResponse("Unable to submit post to API.");
Log.e(TAG, "Unable to submit post to API.");
}
});
}
public void showResponse(String response) {
if (mResponseTv.getVisibility() == View.GONE) {
mResponseTv.setVisibility(View.VISIBLE);
}
mResponseTv.setText(response);
}
}
My other files are pretty much exactly like the files in the tutorial link above. That's what I modified to get to my simple one text field version.
When I am able to get this to contact the API, and I can read the response, then I'll incorporate this into the real app I'm working on.
For now, the app compliles, and runs on my phone(and emulator too). When I submit the phone number, the text field below the submit button doesn't show any message like it should...so I know for sure that theres a problem once
mAPIService.sendPhoneNumber(phone_number).enqueue(new Callback<Post>() {
#Override
public void onResponse(Call<Post> call, Response<Post> response)
{
is reached in MainActivity.
I think that this api requires parameters in JsonObject. so try this
In your APIService
#POST("/verifications/signup/send")
Call<JsonObject> sendPhoneNumber(#Body JsonObject phone_number);
And when sending data use this
JsonObject object=new JsonObject();
object.addProperty("phone_number",yourPhoneNumber);
and in your send post method
mAPIService.sendPhoneNumber(object).enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
int statusCode = response.code();
if (response.isSuccessful()) {
showResponse("Response code is " + statusCode + ". Submitted successfully to API - " + response.body().toString());
Log.i(TAG, "post submitted to API." + response.body().toString());
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
showResponse("Unable to submit post to API.");
Log.e(TAG, "Unable to submit post to API.");
}
});
}
Please try and let me know if it is working.
According to the JSON schema with the phone number, you need to pass the phone number in the body of the API. Instead of using a #Field annotation, use a #Body annotation where the parameter will be an instance of the RequestBody class.
#Field documentation https://square.github.io/retrofit/2.x/retrofit/retrofit2/http/Field.html
Create the new RequestBody class with field phone number.
public class RequestBody {
#Expose
#SerializedName("phone_number")
private String phoneNumber;
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
From the activity when you want to pass the phone number, create an object of the RequestBody class, pass the phone number in the setPhoneNumber() method. Then pass this object in the APIService as a parameter.
In MainActivity.class,
public void sendPost(String phone_number) {
RequestBody requestBody = new RequestBody();
requestBody.setPhoneNumber(phone_number);
mAPIService.sendPhoneNumber(requestBody).enqueue(new Callback<Post>() {
#Override
public void onResponse(Call<Post> call, Response<Post> response)
{
Your APIService will thus look like
public interface APIService {
#POST("/verifications/signup/send")
#FormUrlEncoded
Call<Post> sendPhoneNumber(#Body RequestBody requestBody);
}
Sorry for my English. I want use this service. For determine the language of the text.
Request(Curl):
curl -X POST -d "outputMode=json" --data-urlencode text#ibm.txt -d "url=http://www.ibm.com/us-en/" "https://gateway-a.watsonplatform.net/calls/text/TextGetLanguage?apikey=%API_KEY%"
I use Retrofit for request.
public interface LanguageDetectionApi {
public static final String ROOT_URL = "https://gateway-a.watsonplatform.net/calls/";
#POST("/text/TextGetLanguage")
Call<List<PostModel>> getData(#Query("apikey") String apikey, #Query("text") String text);
}
Create retrofit object:
public class App extends Application {
private static LanguageDetectionApi _languageDetectionApi;
private Retrofit _retrofit;
#Override
public void onCreate() {
super.onCreate();
_retrofit = new Retrofit.Builder()
.baseUrl(_languageDetectionApi.ROOT_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
_languageDetectionApi = _retrofit.create(LanguageDetectionApi.class);
}
public static LanguageDetectionApi getLanguageDetectionApi() {
return _languageDetectionApi;
}
}
And send request:
App app = new App();
app.onCreate();
app.getLanguageDetectionApi().getData("4978e60252ae102dfe1341146bb8cc3ec4bbbd78", textForRecognition).enqueue(new Callback<List<PostModel>>() {
#Override
public void onResponse(Call<List<PostModel>> call, Response<List<PostModel>> response) {
List<PostModel> posts = new ArrayList<>();
posts.addAll(response.body());
}
#Override
public void onFailure(Call<List<PostModel>> call, Throwable t) {
Toast.makeText(MainActivity.this, "An error occurred during networking", Toast.LENGTH_SHORT).show();
}
});
PostModel i generated in site http://www.jsonschema2pojo.org/.
Questions:
No response comes to me, although apikey are exactly valid.
How to specify in the interface parametr "outputMode=json"?
And I translated correctly cURL to LanguageDetectionApi?
It seems to me that the whole mistake in the class LanguageDetectionApi. Can you help deal with this? Thank you!
change url code like below:
public interface LanguageDetectionApi {
public static final String ROOT_URL = "https://gateway-a.watsonplatform.net";
#POST("/calls/text/TextGetLanguage")
Call<List<PostModel>> getData(#Query("apikey") String apikey, #Query("text") String text);
}
base url should be ony host name.
this is my code:
the interface :
public interface LoginAPI {
#GET("LoginCheck/{username}/{password}/{status}")
Call<List<Login>> LoginCheck(#Path("username") String username, #Path("password") String password, #Path("status") String status);
}
the class:
public class Login {
String username;
String password;
String status;
}
the main activity :
private void LoginCheck() {
String baseUrl = "http:192.168.169.3:8889/WebService_Indekost/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
LoginAPI api = retrofit.create(LoginAPI.class);
Call<List<Login>> result = api.LoginCheck("username", "password", "status");
result.enqueue(new Callback<List<Login>>() {
#Override
public void onResponse(Call<List<Login>> call, Response<List<Login>> response) {
Log.d("test",response.message());
}
#Override
public void onFailure(Call<List<Login>> call, Throwable t) {
Log.d("Fail", "Fail");
}
});
}
when i try to run it, it shows fail instead of the message. note that the response should be in json format. what am i doing wrong here?
As you commented your error Its saying Expected BEGIN_ARRAY but was BEGIN_OBJECT means exactly You tried to treat it as an Array which starts with brackets like
[ .. data
]
But you are not getting a JSON Array, you are getting an Object. So Try changing the API call Call<List<Login>> into Call<Login>. That may work.
Because you use List<Login> to parse the response, so the response should be a JSON array, like following:
[
{"username":"...","password":"...", "status":"..."},
{"username":"...","password":"...", "status":"..."},
{"username":"...","password":"...", "status":"..."}
]
if the server return result is like following:
{"username":"...","password":"...", "status":"..."}
then you should use Call<Login> replace Call<List<Login>> to parse the response, or, you let server to change its response format to correspond client, it depends what you really want.