Fetch string data from JSON response retrofit - android

I'm working with JSON response which I can get from my server, at the beginning I have to log in at my application so I use such api:
#Headers("Content-type: application/json")
#POST("/v1/login")
Call<Post> auth(#Body Post body);
and also my POJO-class:
public class Post {
#SerializedName("username")
private String username;
#SerializedName("password")
private String password;
public Post(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername (String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
and after all initialize it at my mainactivity class:
public void sendPost() {
final EditText titleEt = findViewById(R.id.login);
final EditText bodyEt = findViewById(R.id.password);
final String a = titleEt.getText().toString().trim();
final String b = bodyEt.getText().toString().trim();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://server/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService mAPIService = retrofit.create(APIService.class);
//retrofit.create(APIService.class);
mAPIService.auth(new Post(a, b)).enqueue(new Callback<Post>() {
#Override
public void onResponse(#NonNull Call<Post> call, #NonNull Response<Post> response) {
if (response.isSuccessful()) {
Toast.makeText(LoginActivity.this, "Post submitted to API.", Toast.LENGTH_LONG).show();
Intent intent = new Intent(LoginActivity.this, SecondScreen.class);
findViewById(R.id.btn_submit).getBackground().setColorFilter(Color.parseColor("#1cd000"), PorterDuff.Mode.MULTIPLY);
startActivity(intent);
saveData();
/* try {
String responseString = String.valueOf(response.body());
TextView txt = findViewById(R.id.post);
txt.setText(responseString);
}
catch (Exception e){
e.printStackTrace();
}*/
} else {
Toast.makeText(LoginActivity.this, "Unable to submit post to API.Error!!!", Toast.LENGTH_LONG).show();
findViewById(R.id.btn_submit).getBackground().setColorFilter(Color.parseColor("#FF0000"), PorterDuff.Mode.MULTIPLY);
}
}
#Override
public void onFailure(#NonNull Call<Post> call, #NonNull Throwable t) {
Toast.makeText(LoginActivity.this, "Unable to submit post to API.", Toast.LENGTH_LONG).show();
}
});
}
as you can see I commented my trying to fetch some data from JSON response, in general, I would like to get my access token from the response and I also wouldn't like to create some classes for fetching if it is possible, because I have already initialized my retrofit at my MainActivity class. After logging in a can get my received and sent messages in JSON too, but I would like to insert this data into the simple listview. So I hope that somebody at this forum will help me with my problem. Sorry for my maybe bad English.

You have to understand that each Retrofit request is asynchronous, that means, it will eventually be executed while your application runs. You should use RxJava to help you with that, since you should use to observe the data you need to get from your API.
Important Note: Also updating UI in your response from retrofit might trigger exceptions due to not running in Main Thread.
Some useful links to implement what you need :
https://www.toptal.com/android/functional-reactive-android-rxjava
https://www.journaldev.com/20433/android-rxjava-retrofit
https://medium.com/3xplore/handling-api-calls-using-retrofit-2-and-rxjava-2-1871c891b6ae

Related

Android Java Retrofit login with bearer token - Spring Boot Backend JWT token

i've seend some somehow similar but still different questions here, and sadly non of their answers worked for me.
For a project at the university i need to implement login and registration for an android app with a spring boot backend which uses JWT Token (when I use Postman it shows a bearer token with OAuth 2.0 authhorization, actually with postman everything works fine, so backend is running correctly - and Registration is working fine, but it's logic is much simpler).
Basically if someone could direct me to a good tutorial how to implement login for android to a backend which response with a bearer token i would be very very very thankful. I did search google but did never find a whole tutorial on how to implement this.
So far i got this:
the api interface:
public interface TheApi {
#POST("/login")
#FormUrlEncoded
Call<LoginResponse> login(
#Field("username") String username,
#Field("password") String password);
}
The Main Activity with the Call:
public class MainActivity extends AppCompatActivity {
private TextView textViewResult;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textViewResult = findViewById(R.id.text_view_result);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.0.75:8080/")
.addConverterFactory(GsonConverterFactory.create())
.build();
TheApi theApi = retrofit.create(TheApi.class);
LoginRequest loginRequest = new LoginRequest("admin#admon.com", "admin123");
Call<LoginResponse> call = theAwesomeApi.login(loginRequest.getUsername(), loginRequest.getPassword());
call.enqueue(new Callback<LoginResponse>() {
#Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (!response.isSuccessful()) {
textViewResult.setText("Code: " + response.body() + response.code());
return;
} else {
textViewResult.setText("error login");
}
}
#Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
textViewResult.setText("error connection");
}
});
The LoginRequest Class:
public class LoginRequest {
#Expose
#SerializedName("username")
private String username;
#Expose
#SerializedName("password")
private String password;
public LoginRequest(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
And the LoginResponse Class:
public class LoginResponse {
private String authToken;
public LoginResponse(String authToken) {
this.authToken = authToken;
}
public String getAuthToken() {
return authToken;
}
public void setAuthToken(String authToken) {
this.authToken = authToken;
}
}
Yes, I did not implement anything useful to save the bearer token for reuse later, but I first wanted to check if I could login somehow.
And sadly the backend responds only with a 403 error code
And the backends logs this error:
2020-06-30 18:56:42.618 ERROR 13141 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] >: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
java.lang.NullPointerException: null
at >com.theawesomelibrary.awesomebackend.security.JwtAuthenticationFilter.attemptAuthentication(Jwt>AuthenticationFilter.java:49) ~[classes/:na]
It seems like no username or password is sent, but i dont' understand what i did wrong and could not find any solution yet. Please need your help dear community.
Could some one point me the solution how to send a correct post request for login
and how to get the bearer token for later use?
Thanks in advance.

How to convert POJO object to json string using gson for a specified field?

I'm using retrofit2 for password recovery, an API request is sent to the server with an email address which is entered by the user.
I'm only setting the email in the POJO ,and after the conversion, the JSON string looks like this:
{"email":"email#email.com", "password":"", "password_confirmation":"", "token":""}
But the JSON I need to send out, should look like this:
{"email":"email#email.com"}
If I'll create another POJO class with an email param then I'll get the required string, but I just want to know if it is possible, using the current POJO.
How would one convert a POJO object to a JSON string using gson for a specified field?
Please Refer to the below code:
public class User {
private String email;
private String password;
private String password_confirmation;
private String token;
public User() {
this.email="";
this.password="";
this.password_confirmation="";
this.token="";
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword_confirmation() {
return password_confirmation;
}
public void setPassword_confirmation(String password_confirmation) {
this.password_confirmation = password_confirmation;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
My Api interface
public interface Api{
#POST("/api/password/create")
#Headers({"Accept:application/json", "Content-Type:application/json"})
Call<User> Create(#Body RequestBody requestBody);
}
My Retrofit method
private void authenticateEmail(final Context context) {
User user=new User();
Api api=new Api();
String email = edt_forgot_email.getText().toString().trim();
Gson gson = new GsonBuilder()
.setDateFormat(SERVICE_DATE_FORMAT)
.setLenient()
.create();
api = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
user.setEmail(email);
Gson lGson = new GsonBuilder().setDateFormat(SERVICE_DATE_FORMAT).create();
String jsonString = lGson.toJson(user);
Log.d("debug", "jsonString==>" + jsonString);
final RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonString);
Call<User> call = api.Create(requestBody);
Log.d("debug", "url: " + call.request().url().toString());
call.enqueue(new Callback<User>() {
#Override
public void onResponse(#NonNull Call<User> call, #NonNull Response<User> response) {
Log.d("debug", "responsecode==>" + response.code());
Log.d("debug", "responsebody==>" + response.body());
if (response.code() == 200) {
String msg = "We have emailed you OTP";
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NonNull Call<User> call, #NonNull Throwable t) {
Toast.makeText(activity, "server error",Toast.LENGTH_SHORT).show();
}
});
}
Remove this code from constructor :
public User() {
this.email="";
this.password="";
this.password_confirmation="";
this.token="";
}
If you want to exclude empty values from output json, you should make it as null.
Good luck!
You can just create a JSON Object for the single field like this:
JSONObject data =new JSONObject();
data.put("email", email#email.com)
send data object to the retrofit.

Is this the right way to set up Basic Authentication with Retrofit 2?

I'm doing a side project and all I know is I should use Basic Authentication. Since I don't have experience using it, I found some stuff online and I want to ask you for your opinion, is this the right way to do it...
So, first thing is from Retrofit's documentation:
This is the Retrofit(network) setup:
public class RetrofitSetup {
public static final String API_BASE_URL = "----";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
public static <S> S createService(Class<S> serviceClass) {
return createService(serviceClass, null, null);
}
public static <S> S createService(
Class<S> serviceClass, String username, String password) {
if (!TextUtils.isEmpty(username)
&& !TextUtils.isEmpty(password)) {
String authToken = Credentials.basic(username, password);
return createService(serviceClass, authToken);
}
return createService(serviceClass, null);
}
public static <S> S createService(
Class<S> serviceClass, final String authToken) {
if (!TextUtils.isEmpty(authToken)) {
AuthenticationInterceptor interceptor =
new AuthenticationInterceptor(authToken);
if (!httpClient.interceptors().contains(interceptor)) {
httpClient.addInterceptor(interceptor);
builder.client(httpClient.build());
retrofit = builder.build();
}
}
return retrofit.create(serviceClass);
}
}
And Authentication Interceptor:
public class AuthenticationInterceptor implements Interceptor {
private String authToken;
public AuthenticationInterceptor(String token) {
this.authToken = token;
}
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request original = chain.request();
Request.Builder builder = original.newBuilder()
.header("Authorization", authToken);
Request request = builder.build();
return chain.proceed(request);
}
}
This is my RetrofitSerive class:
public interface RetrofitService {
#GET("login")
Call<Void> basicLogin();
#GET("contact")
Call<List<Contacts>> getContacts(#Header("Authorization") String authkey);
#GET("product")
Call<List<Products>> getProducts(#Header("Authorization") String authkey);
}
And the class I found online for generating auth key:
public class Helper {
public static String getAuthToken(String username, String password) {
byte[] data = new byte[0];
try {
data = (username + ":" + password).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "Failed to authenticate";
}
return "Basic " + Base64.encodeToString(data, Base64.NO_WRAP);
}
}
And I believe there is nothing to be changed here... So first thing is to login (check authentication), here is the couple of stuff I want to know how to do right:
private void login(final String username, final String password) {
RetrofitService loginService =
RetrofitSetup.createService(RetrofitService.class, username, password);
Call<Void> call = loginService.basicLogin();
call.enqueue(new Callback<Void>() {
#Override
public void onResponse(#NonNull Call<Void> call, #NonNull Response<Void> response) {
progressBar.setVisibility(View.GONE);
if (response.isSuccessful()) {
// user object available
editor.putString("username", username);
editor.putString("password", password);
editor.apply();
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
} else {
Toast.makeText(LoginActivity.this, response.message(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NonNull Call<Void> call, #NonNull Throwable t) {
progressBar.setVisibility(View.GONE);
Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
If response is successfull, I save those credentials in SharedPreferences..
Next page shuld call another request, so I wonder if there is any difference if I send both username/password or auth key?
This is how I did it:
private void getContacts() {
prefs = getActivity().getSharedPreferences(KEY, MODE_PRIVATE);
String username = prefs.getString("username", null);
String password = prefs.getString("password", null);
RetrofitService loginService =
RetrofitSetup.createService(RetrofitService.class, Helper.getAuthToken(username, password));
Call<List<Contacts>> call = loginService.getContacts(Helper.getAuthToken(username, password));
call.enqueue(new Callback<List<Contacts>>() {
#Override
public void onResponse(#NonNull Call<List<Contacts>> call, #NonNull Response<List<Contacts>> response) {
if (response.isSuccessful()) {
kontaktiAdapter.setKontakti(response.body());
} else {
Toast.makeText(getActivity(), response.message(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NonNull Call<List<Contacts>> call, #NonNull Throwable t) {
Toast.makeText(getActivity(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
So in this call, instead of sending username/password to RetrofitSetup.createService, I'm sending Helper.getAuthToken(username, password) with username and password from SharedPreferences.
Is this the right way to do this? If you exit the app, in login screen I'm checking if SharedPreferences contains username/password and try login with those parameteres.. If I want to log out, I clear those parameters from SharedPreferences so next time user opens the app, SharedPreferences won't contain them, so user won't be logged in, he/she would have to type those again...
What are your thoughts about this, is there anything I should do differently?
Regards!
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class)
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.

Android Send String to Server via retrofit

i want to send string from app to server with retrofit 2, and get back return values. what is the problem?
but it doesn't work.
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RatingApiService retrofitService=retrofit.create(RatingApiService.class);
Call<String> call = retrofitService.registration("saeed","ali");
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response!=null){
Log.i("upload","is success:" +response.body());
}else{
Log.i("upload","response is null");
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.i("upload","onFailure: "+t.getMessage());
}
});
Interface:
public interface RatingApiService {
#FormUrlEncoded
#POST("android/add2/{email}{password}")
Call<String> registration(#Path("email") String email, #Path("password") String password);
}
Instead of using parameters to pass your data; put it in a req.body, then send it. By doing so, you can bypass the URL string limit and the code is much more organized.
public interface RatingApiService {
#Headers("Content-Type: application/json") //Must be set to application/json so req.body can be read.
#POST("android")
Call<String> registration(#Body UserRegistrationRequest body);
}
Now, you might ask what is the " UserRegistrationRequest" class? that will be the object class that will be sent as a JSON object. We define it by:
public class UserRegistrationRequest {
final String email;
final String password;
UserRegistrationRequest(String email, String password){
this.email = email;
this.password = password;
}
}
And then the final step. This is how you convert the object to a JSON object and send it to your server.
UserRegistrationRequest userRegistrationRequest = new UserRegistrationRequest(
RegisterNameFragment.sFirstName, RegisterNameFragment.sLastName, RegisterEmailFragment.sEmail,
RegisterPasswordFragment.sPassword, RegisterAgeFragment.sAge);
Call<Void> call = retrofit.insertUserRegistration(userRegistrationRequest);
call.enqueue(new Callback<UserRegistration>() {
#Override
public void onResponse(Call<UserRegistration> call, Response<UserRegistration> response) {
Log.d("blue", "Data is sent");
}
#Override
public void onFailure(Call<UserRegistration> call, Throwable t) {
Log.d("blue", "fail");
}
});
You need to add #Body annotation to your method like below.
#POST("users/new")
Call<User> createUser(#Body User user);
The content of #Body is sent to server as request body.
If you want to send email and password to server, you should create object that contains such data and use the object in the #Body annotation.

Android Retrofit JSON POST code=400, message=Bad Request

I am new to Adroid developing. I have a login form which I have to post to server. I am using Retrofit 2 library. I get response code=400, message=Bad Request. What am i doing wrong?
Here is my interface:
public interface LoginService {
#POST("login")
Call<Login> createLogin(#Body Login login);
}
My Activity:
Login login = new Login(email, password);
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("https://xxxxxx.herokuapp.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
LoginService loginClient = retrofit.create(LoginService.class);
Call<Login> call = loginClient.createLogin(login);
call.enqueue(new Callback<Login>() {
#Override
public void onResponse(Call<Login> call, Response<Login> response) {
Log.d("response",response.toString());
}
#Override
public void onFailure(Call<Login> call, Throwable t) {
Toast.makeText(LoginActivity.this,":(",Toast.LENGTH_SHORT).show();
}
});
And my Login class:
public class Login {
private String email;
private String password;
public Login(String email, String password){
this.email = email;
this.password = password;
}
For login, app has to send json comprised like
{"email" : "x#y.z","password" : "abc",}
Thank you in advance!

Categories

Resources