I want to send a POST to a server with the body that is true or false. I have this code and I use the Volley library
ShoozyHeader() set Content type to text/plain and Accept to text/plain and other headers necessary for the authentication.
If I try on http://requestmaker.com/ and the server responds correctly but I run this code the server responds:
Bad Request - Invalid Header
HTTP Error 400. The request has an invalid header name.
If I remove the getBody() the server respond fine.
final String url = POSTS_URI + idPost + "/likes";
StringRequest strReq = new StringRequest(Request.Method.POST,
url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG, response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
Toast.makeText(act, "Network error", Toast.LENGTH_SHORT).show();
}
}
) {
#Override
public byte[] getBody() throws AuthFailureError {
return "true".getBytes();
}
};
Try this one, it worked fine for me:
JSONObject params = new JSONObject();
try {
params.put("likes", "true");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.POST,
url, params,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d(TAG, response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
}
}) {
/**
* Passing some request headers
* */
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json; charset=utf-8");
return headers;
}
};
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(60000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
// Adding request to request queue
getRequestQueue(jsonObjReq);
Have you override getHeaders in your class that extends the Request ?
Here is how to:
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
Here is an example for seting up a Base64 (username, password) header:
#Override
private HashMap<String, String> getHeaders() {
HashMap<String, String> headers = new HashMap<>();
headers.put("Authorization", getAuthentication("usernameExample", "passwordExample"));
headers.put("Content-Type", "application/json; charset=utf-8");
Log.d(TAG + "");
return headers;
}
private String getAuthentication(String username, String password) {
String credentials = String.format("%s:%s", username, password);
String auth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.DEFAULT);
return auth;
}
Related
at the first I am sorry for my poor english.
I am developing an android app with nodejs backend.JWT is used for authorization, so I send access token to the server to authorize my request. access token life is short and expire in 60s. for refreshing the access token the app must to send refresh token to server. for this purpose I am using OkHttp authenticator but authenticator is not work. can any body help me to solve this problem
ServiceGenerator
public class ServiceGenerator {
private static final OkHttpClient httpClient =
new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.build();
private static final Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
public static Retrofit retrofit = builder.build();
public static <S> S createService(Class<S> serviceClass) {
Retrofit retrofit = builder.client(httpClient).build();
return retrofit.create(serviceClass);
}
public static <S> S createServiceWithAuth(Class<S> serviceClass, String accessToken, Context context) {
OkHttpClient newClient = httpClient.newBuilder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder builder = request.newBuilder();
if(!accessToken.isEmpty()){
builder.addHeader("x-auth-token", accessToken);
}
request = builder.build();
return chain.proceed((request));
}
}).authenticator(CustomAuthenticator.getInstance(accessToken, context))
.build();
Retrofit newRetrofit = retrofit.newBuilder().client(newClient).baseUrl(API_BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
return newRetrofit.create(serviceClass);
}
}
CustomAuthenticator
public class CustomAuthenticator implements Authenticator {
private String accessToken;
private static CustomAuthenticator INSTANCE;
Context context;
private CustomAuthenticator(String accessToken, Context context){
this.accessToken = accessToken;
Hawk.init(context).build();
this.context = context;
}
static synchronized CustomAuthenticator getInstance(String accessToken, Context context){
if(INSTANCE == null){
INSTANCE = new CustomAuthenticator(accessToken, context);
}
return INSTANCE;
}
#Nullable
#Override
public Request authenticate(#Nullable Route route, #NotNull Response response) throws IOException {
if(responseCount(response) >= 2){
return null;
}
ApiClient client = ServiceGenerator.createService(ApiClient.class);
User user = new User();
user.setRefreshToken(Hawk.get("RefreshToken"));
Call<User> refreshAccessTokenCall = client.refresh(user);
retrofit2.Response<User> res = refreshAccessTokenCall.execute();
if(res.isSuccessful()){
assert res.body() != null;
String accessToken = res.body().getAccessToken();
Hawk.put("AccessToken", accessToken);
return response.request().newBuilder().addHeader("x-auth-token", res.body().getAccessToken()).build();
}else{
return null;
}
}
private int responseCount(Response response){
int result = 1;
while ((response = response.priorResponse()) != null){
result++;
}
return result;
}
}
and method that POST data to server (in Fragment)
private void saveExpert() {
if (!validate()) {
return;
}
progressBar.setVisibility(View.VISIBLE);
Objects.requireNonNull(getActivity()).getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
String first_and_last_name = firstAndLastName.getText().toString();
String national_code = nationalCode.getText().toString();
String phone_number = phoneNumber.getText().toString();
String home_office_city = homeOfficeCity.getText().toString();
String _address = address.getText().toString();
String _field = field.getText().toString();
String sub_field = subField.getText().toString();
String _grade = grade.getText().toString();
ApiClient client = ServiceGenerator.createServiceWithAuth(ApiClient.class, Hawk.get("AccessToken"), getContext());
SetExpert setExpert = new SetExpert(first_and_last_name, national_code, phone_number, home_office_city, _address, _field, sub_field, _grade);
Call<Expert> expertCall = client.saveExpert(setExpert);
expertCall.enqueue(new Callback<Expert>() {
#Override
public void onResponse(#NotNull Call<Expert> call, #NotNull Response<Expert> response) {
if (response.isSuccessful()) {
Expert expert = response.body();
if (expert != null) {
Hawk.init(Objects.requireNonNull(getContext())).build();
Hawk.put("ExpertNationalCode", expert.getNational_code());
progressBar.setVisibility(View.GONE);
Objects.requireNonNull(getActivity()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
new MaterialDialog.Builder(Objects.requireNonNull(getContext()))
.typeface("IRANSansWebMedium.ttf", "IRANSansWeb.ttf")
.title("پیام سیستم")
.content(expert.getFirst_and_last_name() + " عزیز با تشکر از ثبت نام شما. این ثبت نام به منزله تایید نهایی نبوده و پس از بررسی مشخصات و مدارک، با شما تماس گرفته خواهد شد.")
.positiveText("تایید.")
.canceledOnTouchOutside(false)
.onPositive(new MaterialDialog.SingleButtonCallback() {
#Override
public void onClick(#NonNull MaterialDialog dialog, #NonNull DialogAction which) {
dismiss();
}
})
.show();
}
} else {
ApiError apiError = ErrorUtils.parseError(response);
progressBar.setVisibility(View.GONE);
Objects.requireNonNull(getActivity()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
Toast.makeText(getContext(), apiError.getMessage(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NotNull Call<Expert> call, #NotNull Throwable t) {
progressBar.setVisibility(View.GONE);
Objects.requireNonNull(getActivity()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
removeHeader("x-auth-token") may solve this.
return response.request().newBuilder()
.removeHeader("x-auth-token") // Remove old header
.addHeader("x-auth-token", res.body().getAccessToken())
.build();
To prevent old "x-auth-token" from being sent, removeHeader() seems to be needed.
UPDATE:
I have learned what I am looking to do is to use the Async within Retrofit with multiple queries too. I have updated my code, but I cannot get the async with the queries.
I am using Retrofit to make my data calls to a movie database and need to change the sort order depending on user settings. I am not clear how I could add this functionality to my interface.
sort_by=highest_rating.desc
or
sort_by=popularity.desc
Interface:
public interface MovieDatabaseApiCient {
#GET("/3/discover/movie")
void getData(#Query("api_key") String apiKey, #Query("sort_by") String sortByValue, Callback<MovieDbModel> response);
}
UPDATED API INTERFACE:
public interface MovieDatabaseApiCient {
#GET("/3/discover/movie?sort_by=popularity.desc&api_key=xxxxxxx")
void getMoviesByPopularityDesc(Callback<MovieDbModel> response);
#GET("/3/discover/movie?sort_by=vote_average_desc&api_key=xxxxxxxx")
void getMoviesByVotingDesc(Callback<MovieDbModel> response);
}
UPDATED DATA CALL THAT WORKS:
private void makeDataCall(String sortPreference) {
final RestAdapter restadapter = new RestAdapter.Builder().setEndpoint(ENDPOINT_URL).build();
MovieDatabaseApiCient apiLocation = restadapter.create(MovieDatabaseApiCient.class);
if (sortPreference.equals(this.getString(R.string.sort_order_popularity)) ){
apiLocation.getMoviesByPopularityDesc (new Callback<MovieDbModel>() {
#Override
public void success(MovieDbModel movieModels, Response response) {
movieDbResultsList = movieModels.getResults();
MoviesGridViewAdapter adapter = new MoviesGridViewAdapter(getApplicationContext(), R.layout.movie_gridview_item, movieDbResultsList);
gridView.setAdapter(adapter);
}
#Override
public void failure(RetrofitError error) {
Log.d("ERROR", error.toString());
Toast.makeText(getApplicationContext(), "Error: " + error.toString(), Toast.LENGTH_SHORT).show();
}
});
} else {
apiLocation.getMoviesByVotingDesc( new Callback<MovieDbModel>() {
#Override
public void success(MovieDbModel movieModels, Response response) {
movieDbResultsList = movieModels.getResults();
MoviesGridViewAdapter adapter = new MoviesGridViewAdapter(getApplicationContext(), R.layout.movie_gridview_item, movieDbResultsList);
gridView.setAdapter(adapter);
}
#Override
public void failure(RetrofitError error) {
Log.d("ERROR", error.toString());
Toast.makeText(getApplicationContext(), "Error: " + error.toString(), Toast.LENGTH_SHORT).show();
}
});
}
}
My call for the data:
private void makeDataCall (String apiKey, String sortPreference) {
final RestAdapter restadapter = new RestAdapter.Builder().setEndpoint(ENDPOINT_URL).build();
MovieDatabaseApiCient apiLocation = restadapter.create(MovieDatabaseApiCient.class);
apiLocation.getData(apiKey, sortPreference, new Callback<MovieDbModel>){
#Override
public void success(MovieDbModel movieModels, Response response) {
movieDbResultsList = movieModels.getResults();
MoviesGridViewAdapter adapter = new MoviesGridViewAdapter(getApplicationContext(), R.layout.movie_gridview_item, movieDbResultsList);
gridView.setAdapter(adapter);
}
#Override
public void failure(RetrofitError error) {
Log.d("ERROR", error.toString());
Toast.makeText(getApplicationContext(), "Error: " + error.toString(), Toast.LENGTH_SHORT).show();
}
});
}
I found a way to do Synchronously, but not asynchronously.
From your question and comment, IHMO, you should import retrofit.Callback; instead of import com.squareup.okhttp.Callback;
My code as the following has no compile error:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// creating a RestAdapter using the custom client
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL_BASE)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setClient(new OkClient(mOkHttpClient))
.build();
WebService webService = restAdapter.create(WebService.class);
retrofit.Callback<GetRoleData> callback = new Callback<GetRoleData>() {
#Override
public void success(GetRoleData getRoleData, retrofit.client.Response response) {
}
#Override
public void failure(RetrofitError error) {
}
};
webService.getData("api_key", "sort_by", callback);
}
Interface:
public interface WebService {
#GET("/3/discover/movie")
void getData(#Query("api_key") String apiKey, #Query("sort_by") String sortByValue, Callback<GetRoleData> response);
}
So, please check your code again
My backend returns this json object to signal a succesful new user registration
{
"error": false,
"message": "You have been successfully registered"
}
and this to signal the account already exists
{
"error": true,
"message": "Sorry, this email already exists"
}
and this on other registration failure
{
"error": true,
"message": "An error occurred!! Please try again"
}
I want to use retrofit:2.0.0-beta2 to make an asynchronous http call and handle the response to take the use to the home page or redirect to the login page accordingly.
Here's my POJO:
public class User {
public final String email, firstname, lastname, city, birthday, gender, password;
public User(final String email, final String firstname, final String lastname,
final String city, final String birthday, final String gender, final String password) {
this.email = email;
this.firstname = firstname;
this.lastname = lastname;
this.city = city;
this.birthday = birthday;
this.gender = gender;
this.password = password;
}
}
Here's my service:
public interface APIService {
#POST("/api/user")
Call<User> createUser(#Body User user);
}
You need to create a extra class to handled response.
public class ResponseApi{
public String error;
public String message;
}
You're inferface
#POST("/api/user")
Call<ResponseApi> createUser(#Body User user);
You need to implement onResponse listener and handle the redirects or any other action inside it.
For example:
protected void loginAttempt(final View v) {
authUser = new HashMap<>();
authUser.put("email", email.getText().toString());
authUser.put("password", password.getText().toString());
Call call = SubApplication.api.authUser(authUser);
call.enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) {
try {
JSONObject responseObject = new JSONObject(new Gson().toJson(response.body()));
Log.d("LoginActivity", "onResponse: " + responseObject.toString());
if (responseObject.getBoolean("success") == true) {
Toast.makeText(v.getContext(), getString(R.string.error_login_success), Toast.LENGTH_LONG).show();
Intent intent = new Intent(v.getContext(), DashboardActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(v.getContext(), getString(R.string.error_login_failed), Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Log.e("LoginActivity", "Exception: " + e);
Toast.makeText(v.getContext(), getString(R.string.error_login_failed), Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call call, Throwable t) {
Log.e("LoginActivity", "onFailure: " + t);
}
});
I'm using Retrofit and RxJava but can't seem to do what I want.
Here's my declaration of my web service:
Observable<Response> rawRemoteDownload(#Header("Cookie") String token, #Path("programId") int programId);
The problem I have is the webservice is returning a 403 and a json payload with details.
Retrofit calls onError, only passing the Throwable so I can't check the response body.
Here's part of my test code
apiManager.rawRemoteDownloadRequest("token", 1).subscribe(new Observer<Response>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
// this is called and I've lost the response!
}
#Override
public void onNext(Response response) {
}
});
SOLUTION:
Thanks to Gomino, I went with this as a solution:
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
if (throwable instanceof RetrofitError) {
Response response = ((RetrofitError) throwable).getResponse();
System.out.println(convertToString(response.getBody()));
}
}
where convertToString looks like:
private String convertToString(TypedInput body) {
byte[] bodyBytes = ((TypedByteArray) body).getBytes();
return new String(bodyBytes);
}
Check if the throwable is a RetrofitError:
#Override
public void onError(Throwable e) {
if (e instanceof RetrofitError) {
Response response = ((RetrofitError) e).getResponse();
}
}
I am trying to unit test the below ResponseHeaderFilter but find that this unit test fails:
#Test
public void testDoFilterWithOmmissableInitParameters_ETag()
throws IOException, ServletException {
// Add allowableUserAgents initParameters
MockFilterConfig filterConfig = new MockFilterConfig();
filterConfig.addInitParameter("allowableUserAgents", "avaya");
// Add allowableUserAgents initParameters
filterConfig.addInitParameter(OMIT_KEY, OMIT_VALUE);
// Set user-agent
request.addHeader("user-agent", "avaya");
response.addHeader("ETag", "someEtagHash");
filter.init(filterConfig);
filter.doFilter(request, response, chain);
// THIS ASSERTION FAILS:
assertThat((String)response.getHeader("ETag"), is(nullValue()));
}
Specifically, the setHeader() method when I run this in debug mode is NEVER called:
chain.doFilter(request,
new HttpServletResponseWrapper(response) {
public void setHeader(String name, String value) {
//if (!(name != null && omitHeaders.contains(name.toUpperCase()))) {
if (name != null && omitHeaders.contains(name)) {
super.setHeader(name, value);
}
}
});
The debugger gets to the line for new HttpServletResponseWrapper(response) but does not actually execute the setHeader method.
here is the entire class itself:
public class ResponseHeaderFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(ResponseHeaderFilter.class);
Map<String, String> additionalHeaders = new HashMap<String, String>();
Set<String> omitHeaders = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
Set<String> allowableUserAgents = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
boolean allowFromAllUserAgents = false;
public void doFilter(final ServletRequest request,
final ServletResponse res,
final FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
String userAgent = ((HttpServletRequest) request).getHeader("user-agent");
if (allowFromAllUserAgents
|| (userAgent != null && allowableUserAgents.contains(userAgent))
) {
logger.debug("apply ResponseHeader rules for user agent [{}]", userAgent);
for (Map.Entry<String, String> entry : additionalHeaders.entrySet()) {
response.addHeader(entry.getKey(), entry.getValue());
}
chain.doFilter(request,
new HttpServletResponseWrapper(response) {
public void setHeader(String name, String value) {
//if (!(name != null && omitHeaders.contains(name.toUpperCase()))) {
if (name != null && omitHeaders.contains(name)) {
super.setHeader(name, value);
}
}
});
} else {
logger.debug("User agent [{}] is not an allowable agent for this filter", userAgent);
chain.doFilter(request, res);
}
}
/**
* Called once during start-up
*
* #param filterConfig for Filter configuration
*/
public void init(final FilterConfig filterConfig) {
logger.info("*** ResponseHeaderFilter.init() ***");
// set the provided HTTP response parameters
for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements(); ) {
String headerName = (String) e.nextElement();
String headerValue = filterConfig.getInitParameter(headerName);
// Add the list of allowable user-agents
// cannot be null: if (headerName != null) {
if (headerName.equalsIgnoreCase("allowableUserAgents")) {
// omit
parseToUpperCaseElements(headerValue, allowableUserAgents);
logger.debug("allowable user-agent's {}", allowableUserAgents);
} else if (headerName.equalsIgnoreCase("allowFromAllUserAgents")) {
allowFromAllUserAgents = Boolean.parseBoolean(headerValue);
logger.debug("allowFromAllUserAgents {}", allowFromAllUserAgents);
} else if (headerName.equalsIgnoreCase("omit")) {
parseToUpperCaseElements(headerValue, omitHeaders);
logger.debug("Omit headers {}", omitHeaders);
} else {
additionalHeaders.put(headerName, headerValue);
logger.debug("adding header [{}] with value [{}]", headerName, headerValue);
}
//}
}
}
protected final void parseToUpperCaseElements(final String str, final Set<String> elementKeys) {
String[] words = str.split(",");
for (String s : words) {
elementKeys.add(s.trim().toUpperCase());
}
}
public void destroy() {
logger.debug("destroy");
}
}
This filter DOES actually work when running and DOES remove the ETag header, but when I am trying to use Cobertura for code coverage, that method is not tested.
Actually the code you are testing ResponseHeaderFilter only adds or register an anonymous HttpServletResponseWrapper in the FilterChain :
chain.doFilter(request, new HttpServletResponseWrapper(response) {
public void setHeader(String name, String value) { ... }
});
And the FilterChain is executed by the container itself. Your test code does nothing but passing a FilterChain reference.
I don't know the real type of your FilterChain in your test, but instead of asserting that the tag got removed (assertThat((String)response.getHeader("ETag"), is(nullValue()));) I would split your test in two different test.
Replace the anonymous HttpServletResponseWrapper by a full type that can be called EtagRemoverHttpServletResponseWrapper and make a unit test that will actually assert that on wrapper.setHeader(...) it etag header is omitted.
The first on your ResponseHeaderFilter which will assert that the chain contains a reference of type ETagRemoverHttpServletResponseWrapper
Hope that helps.