How to send GET Request with JSON in Header in Retrofit - java

I want to create an app which will locate an Scooters. I am using Bird(Scooter) api. I got an Auth token, but when I want to send request with GET method, with headers, it returns me response code 401, but auth token is not null. Please help me. Api Documentation
My request interface
public interface ApiCallsInterface {
#Headers({
"Device-id:43ba174c-11f4-4918-9fcc-6d785cfc256e"
,"Platform:android","Content-type:application/json"
})
#POST("/user/login")
Call<AuthResponseClass> getAuthToken(#Body Map<String, String> params);
#Headers({
"Device-id:43ba174c-11f4-4918-9fcc-6d785cfc256e",
"App-Version:3.0.5"
})
#GET("/bird/nearby?latitude=37.77184&longitude=-122.40910&radius=1000")
Call<BirdResponse> getBirds(#Header("Authorization") String token, #Header("Location") Map<String, String> params);}
How I send request
Map<String, String> requestParams = new HashMap<>();
requestParams.put("latitude",lat);
requestParams.put("longitude",lng);
requestParams.put("altitude","500");
requestParams.put("accuracy","100");
requestParams.put("speed","-1");
requestParams.put("heading","-1");
apiCallsInterface.getBirds(AUTH_TOKEN,requestParams).enqueue(new Callback<BirdResponse>() {
#Override
public void onResponse(Call<BirdResponse> call, Response<BirdResponse> response) {
Log.d(TAG,"" + response.code());
}
#Override
public void onFailure(Call<BirdResponse> call, Throwable t) {
}
});

Be sure to append "Bird " before your token as in your api documentation.

401 The request requires user authentication
Not adding the header properly here is the guide to generate header properly
https://github.com/ubahnverleih/WoBike/blob/master/Bird.md

Related

How to call List Item in SharePoint Using REST API in Android?

I'm want to consume share point rest API service to call from Android previously i use to call share point web service through the graph API but while generating token from graph API its not support in below URL, does any one have any solution about this problem.
https://mysharepoint.sharepoint.com/sites/MySite/_api/web/lists/getbytitle('Announcements')/Items
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, MSGRAPH_URL,
parameters,new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
/* Successfully called graph, process data and send to UI */
Log.d(TAG, "Response: " + response.toString());
updateGraphUI(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error: " + error.toString());
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + authResult.getAccessToken());
return headers;
}
};
Log.d(TAG, "Adding HTTP GET to Queue, Request: " + request.toString());
request.setRetryPolicy(new DefaultRetryPolicy(
3000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.add(request);
I already tried with MSAL library but its does not work with this token.
Update: i used to call graph api for janrating token but i got 401 error with this above mention URL.
You are calling a SharePoint API, so you will need a SharePoint token instead of a Graph token. These are two separate APIs with different authentications.
To get a SharePoint token you will need to register an App in SharePoint itself or use the users username + password if available in your app.
Also see:
https://spshell.blogspot.com/2015/03/sharepoint-online-o365-oauth.html
https://shareyourpoint.net/2017/01/25/operations-using-rest-in-sharepoint-online-authorization/
For Graph, use an URL like this to get your list items:
https://graph.microsoft.com/v1.0/sites/{site-id}/lists/{list-id}/items?expand=fields(select=Column1,Column2)
You will probably need to do several calls to get your site ID and list ID first.
https://learn.microsoft.com/en-us/graph/api/listitem-list?view=graph-rest-1.0

Spring Boot : Token authentication(bearer) in request headers in rest api when token also comes from calling another api

In my spring boot Application i have a scheduler which calls an API to generate token which expires in 15 min. Time of scheduler is also 15 min. please find below sample:
public class TokenGeneration {
private static String token = null;
#Scheduled(15 minutes)
public String fetchToken() {
// api call which return token
HttpEntity<model> response = restTemplate.exchange(uri, POST, entity, model.class);
token = response.getBody().getAccessToken();
}
}
I stored token value in static variable from a non static method so that i can use this token variable wherever i want to use token value. is this right approach ? if not plz let me know how i can achieve this.
Do i need to make TokenGeneration class singleton so that only one instance of this class is made throught application?
Also i want to create an interceptor or filter in which i can set Authorization headers and token value so that each request will populate authorization header automatically, i don't want to set authorization header in each request like this :
HttpHeaders headers = new HttpHeaders();
headers.set(CpsConstant.AUTHORIZATION, CpsConstant.BEARER + token);
So i tried with this custom interceptor :
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String token = TokenGeneration.token;
request.getHeaders().add("Authorization", "Bearer " + token);
return execution.execute(request, body);
}
will add this interceptor in restTemplate in config file.
So is this right approach for both token generation as well as setting headers for each request or any improvements need to be done in this approach ?
Me thinking of calling token generation method in interceptor in case of token is null like :
if(token == null){
//call token generation fetchToken method
}
It is the right approach
Spring default scope is always singleton if not specified
It is ok to use interceptor, but what if you want to call a API without a token?
Best approach to use two separate methods to send request with token and without token using a separate class
#Component
public class RestClient {
#Autowired
RestTemplate restTemplate;
public HttpHeaders getRequestHeaderBearer() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER +
TokenGeneration.token);
return headers;
}
public HttpHeaders getRequestHeader() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public <T> ResponseEntity<T> restExchangeBearer(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeaderBearer()), classObj);
}
public <T> ResponseEntity<T> restExchange(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeader()), classObj);
}
}

Post request with query params and body

I am making use of Retrofit to call the restful webservices in the android, I have come across the scenario like I need to pass query params and request payload object in the retrofit request, so I've tried something like this
#POST("actual url")
Call<ReceiptList> getData(#Query("limit") String limit,
#Query("page") String page,
#Body ReceiptRequestPayload receiptRequestPayload);
Calling API
Call<cutomObject> responseCall = API.getData("10", "1", requestPayload);
responseCall .enqueue(new Callback<cutomObject>() {
#Override
public void onResponse(Call<cutomObject> call, retrofit2.Response<cutomObject> response) {
Log.d(TAG, "onResponse: Receipts"+response);
Log.d(TAG, "onResponse: Receipts"+response.body());
}
#Override
public void onFailure(Call<ReceiptList> call, Throwable t) {
}
});
But it's not working.
Thanks in advance
Use OkHttp Logging Interceptor to log your HTTP request and response data

Reddit Api GET request succeeds in Postman, but fails with Retrofit

I am attempting to get a user's Reddit front page. I have successfully received an Auth Token via the Token Retrieval (code flow). I have managed to get the expected JSON response via Postman, but cannot produce the same results with Retrofit. The request seems to be timing out as onFailure() is being triggered in the callback. I am using the scopes: identity, mysubreddits, and read.
Additional note: I have got a 401 and 403 response with the code below when using insufficient scopes and using an expired Auth Token respectively.
Relevant constants:
redditToken = (actual auth token String)
RedditConstants.REDDIT_BASE_URL_OAUTH2 = "https://oauth.reddit.com"
Relevant method Section:
if (redditToken != null) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(RedditConstants.REDDIT_BASE_URL_OAUTH2)
.addConverterFactory(GsonConverterFactory.create())
.build();
Api api = retrofit.create(Api.class);
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "bearer " + redditToken);
headers.put("User-Agent", RedditConstants.REDDIT_USER_AGENT);
Call<RedditFeed> call = api.getFeed(headers);
call.enqueue(new Callback<RedditFeed>() {
#Override
public void onResponse(Call<RedditFeed> call, Response<RedditFeed> response) {
Log.d("FINDME", "response "+ response.toString());
if (response.isSuccessful()) {
Log.d("FINDME", "response was a success! we got the feed!");
} else {
Log.d("FINDME", "responce was not successfull triggered");
}
}
#Override
public void onFailure(Call<RedditFeed> call, Throwable t) {
Log.d("FINDME", "onFailure called from populateRedditFeed");
}
});
} else {
Toast.makeText(this, "Please Login with Reddit", Toast.LENGTH_SHORT).show();
}
Retrofit Interface:
public interface Api {
#GET(".")
Call<RedditFeed> getFeed (
#HeaderMap Map<String, String> headers
);
}
Log Results:
D/NetworkSecurityConfig: No Network Security Config specified, using
platform default
I/zygote: Do full code cache collection, code=123KB, data=105KB
After code cache collection, code=111KB, data=79KB
D/FINDME: onFailure called from populateRedditFeed
Postman Success:
After many starts and stops, seemingly randomly getting either a 200 or calling onFailure() I discovered the problem in one of my Retrofit model classes. The JSON response from Reddit contains a field that can either be a long or boolean. I had it defined as a boolean in my java class which threw an llegalStateException when it was returned as a long.
type name description
special edited false if not edited, edit date in UTC epoch-seconds
otherwise. NOTE: for some old edited comments on reddit.com, this will
be set to true instead of edit date.
*I'm unsure how to deal with this duality of types in java so for now I've commented out the field and the code works as expected.

Android Volley POST Request returning error 400 (Bad Request)

I'm Trying to make a POST Request to Azure IoT Hub with a JAVA android application, using Volley Library, but I'm getting this error:
BasicNetwork.performRequest: Unexpected response code 400 for https://elca-iot.azure-devices.net/devices/elca-main-device/messages/events?api-version=2016-02-03)
To access the IoT Hub I need to use a SAS Key that I need to include in the header of the request. The android application Code is below:
RequestQueue queue = Volley.newRequestQueue(c);
String url = "https://elca-iot.azure-devices.net/devices/" + deviceId + "/messages/events?api-version=2016-02-03)";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.POST,
url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
onPostResponse(response, c, true);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("Error", error.getMessage());
onPostResponse(error.getMessage(), c, false);
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
//params.put("Content-Type", "application/xml");
params.put("Authorization", "[My SAS Key]");
params.put("Cache-Control", "no-cache");
return params;
}
/*
#Override
public Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("api-version","2016-02-03");
return params;
}*/
#Override
public String getBodyContentType() {
return "application/text; charset=UTF-8";
}
#Override
public byte[] getBody(){
return "On".getBytes();
}
};
try {
Log.d("request", String.valueOf(stringRequest.getMethod()==Request.Method.POST));
} catch (Exception authFailureError) {
authFailureError.printStackTrace();
}
// Add the request to the RequestQueue.
queue.add(stringRequest);
I've tried to do the same request using POSTMAN and it worked and I don't know why it isn't working with the Android Application. Here's the http code of the request made with POSTMAN:
POST /devices/elca-main-device/messages/events?api-version=2016-02-03 HTTP/1.1
Host: elca-iot.azure-devices.net
Authorization: [My SAS Key]
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: d0aa354a-f79c-e574-893a-c259232aa5cb
on
EDIT:
Screenshot of POSTMAN
It seems the problem lies in the value in the map. [] could not be escaped.
params.put("Authorization", "[My SAS Key]");
What I did was, while creating the params, I encoded key and value with UTF-8.
params.put("Authorization", URLEncoder.encode("[My SAS Key]", "UTF-8"));
I also struggled with this problem for 3 days. It may seem like a work around but it worked for me.
It seems the problem is with body content type
Try adding it both in getHeaders and getBodyContentType
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
//params.put("Content-Type", "application/xml");
params.put("Authorization", "[My SAS Key]");
params.put("Cache-Control", "no-cache");
params.put("Content-Type", "application/x-www-form-urlencoded");
return params;
}
/*
#Override
public Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("api-version","2016-02-03");
return params;
}*/
#Override
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=UTF-8";
}
Android Volley POST request set Content-Type to value "application/json; charset=UTF-8" by default. If you set content type in your request call it will override your content type.
For example if your send request "Content-Type", "application/json" then it will set the content type to "application/json". But if you remove it then automatically it set to "application/json; charset=UTF-8". Problem here is if your back end configured(written by the developers) only to manage "application/json" request then the default call "application/json; charset=UTF-8" would fail your request.(Problem with the back end development). But this will work fine in the POSTMAN. So can't figure out what is actually wrong here. Since you can send a post request by setting "Content-Type" to "application/json" then depend on Volley version your call will fail. Only Volley version 1.1.1 will support override. ('com.android.volley:volley:1.1.1'). So my advice is you have to follow 3 steps.
Send your POST request with out content-type and check the response. If it is 400 it is a problem with your back end configuration.
Change your volley version to 'com.android.volley:volley:1.1.1' in your build graddle
Send POST request again as a header like this.
customHeaders.put("Content-Type","application/json");
It should work now.
According to the Common error codes of Azure IoTHub, the 400 error code means "The body of the request is not valid; for example, it cannot be parsed, or the object cannot be validated."
Meanwhile, as the offical REST reference for Common parameters and headers said, "Set the Content-Type header to application/json."
So please try to change your code as below.
Add the Content-Type header in the getHeaders method.
params.put("Content-Type", "application/json");
Change the reture value of the getBodyContentTypemethod, and use getBytes with UTF-8 in the getBody method.
#Override
public String getBodyContentType() {
return "application/json; charset=UTF-8";
}
#Override
public byte[] getBody(){
return "On".getBytes("UTF-8");
}

Categories