Android and REST service call - code review - android

I have asked a similar question but the answer did not help me. I am trying to call a rest service from my android app. The rest service looks like this:
#RestController
#RequestMapping("/login")
public class LoginController {
#ResponseBody
#RequestMapping(value = "/user", method = RequestMethod.GET)
public boolean getUser(#RequestParam(value = "name", required = true) String userName, #RequestParam(value = "password", required = true) String password) {
if (userName.equals("MMM") && password.equals("mmm")) {
return true;
} else {
return false;
}
}
}
I start my REST service and I try to call it from my android app like this:
public void invokeWS(RequestParams params){
AsyncHttpClient client = new AsyncHttpClient();
client.setConnectTimeout(7000);
client.get("https://ipaddress:8080/login/user/",params ,new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
// Hide Progress Dialog
prgDialog.hide();
try {
JSONObject obj = new JSONObject(responseBody.toString());
if(obj.getBoolean("status")){
Toast.makeText(getApplicationContext(), "You are successfully logged in!", Toast.LENGTH_LONG).show();
// Navigate to Home screen
navigatetoHomeActivity();
}
// Else display error message
else{
errorMsg.setText(obj.getString("error_msg"));
Toast.makeText(getApplicationContext(), obj.getString("error_msg"), Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
// TODO Auto-generated catch block
Toast.makeText(getApplicationContext(), "Error Occured [Server's JSON response might be invalid]!", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
prgDialog.hide();
if(statusCode == 404){
Toast.makeText(getApplicationContext(), "Requested resource not found", Toast.LENGTH_LONG).show();
}
else if(statusCode == 500){
Toast.makeText(getApplicationContext(), "Something went wrong at server end", Toast.LENGTH_LONG).show();
}
// When Http response code other than 404, 500
else{
Toast.makeText(getApplicationContext(), statusCode + "Unexpected Error occcured! [Most common Error: Device might not be connected to Internet or remote server is not up and running]", Toast.LENGTH_LONG).show();
// Toast.makeText(getApplicationContext(), "Unexpected Error occcured! [Most common Error: Device might not be connected to Internet or remote server is not up and running]", Toast.LENGTH_LONG).show();
}
}
});
}
The status code that I receive is 0.
When I use an API to test the REST service it is OK. I do not get any errors. I am on the same network and the firewall is turned off.

To understand what is going wrong you need to investigate in your "error" object in onFailure method
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Log.i("ws", "---->>onFailure : " + error);
}
});
SUGGESTIONS:
1) Do not send password in GET!
The password must not be part of the url, please use POST and send the password in the request body
2) Your android code looks good, but it exist a native framework to call REST services called "VOLLEY"
Volley offers the following benefits:
Automatic scheduling of network requests.
Multiple concurrent network connections.
Transparent disk and memory response caching with standard HTTP cache coherence.
Support for request prioritization.
Cancellation request API. You can cancel a single request, or you can set blocks or scopes of requests to cancel.
Ease of customization, for example, for retry and backoff.
Strong ordering that makes it easy to correctly populate your UI with data fetched asynchronously from the network.
Debugging and tracing tools.
the code is not more complex than yours...
EXAMPLE:
String url = "http://my-json-feed";
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
mTextView.setText("Response: " + response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// TODO: Handle error
}
});
https://developer.android.com/training/volley/

I can see you are doing a couple of "bad practices" here.
Problem 1
I can see your Spring Controller is wanting a Username and Password as URL parameters. This is a no. A server may log URL/endpoint requests which will now contain your credentials in plain text.
Fix option
Very common -> Use an Authorization header (most common: See Basic Auth) over HTTPS.
Less common -> Pass the username and password in a POST body.
Problem 2
Not using the framework as intended
I'd suggest using Spring Security. It will make your stuff a lot more secure and worth doing in the long run. It will take care of authentication, roles and password hashing all for you.

Related

Building an Android Chat App with Pusher: Not Working

Following this tutorial: https://blog.pusher.com/build-an-android-chat-app-with-pusher/ , I have been debugging for hours to find out why it is not working.
From the client I do the following:
private void postMessage() {
String text = messageInput.getText().toString();
// return if the text is blank
if (text.equals("")) {
return;
}
RequestParams params = new RequestParams();
// set our JSON object
params.put("text", text);
params.put("name", "DogLover69");
params.put("time", new Date().getTime());
// create our HTTP client
AsyncHttpClient client = new AsyncHttpClient();
client.post(MESSAGES_ENDPOINT, params, new JsonHttpResponseHandler(){
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
runOnUiThread(new Runnable() {
#Override
public void run() {
messageInput.setText("");
}
});
}
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
Toast.makeText(
getApplicationContext(),
"Something went wrong :(",
Toast.LENGTH_LONG
).show();
}
});
}
Note: MESSAGES_ENDPOINT is the ip address of the server + the port I'm connecting to, and I have confirmed it is correct.
From the server I do the following:
var Pusher = require('pusher');
var pusher = new Pusher({
appId: "421748",
key: "4e789b8f7019463f945b",
secret: "fff93ef698f9231c00dd"
});
...
app.post('/messages', function(req, res){
var message = {
text: req.body.text,
name: req.body.name
}
console.log(message);
pusher.trigger('messages', 'new_message', message, function(err){
console.log("bananas");
done(err);
});
res.json({success: 200});
});
The server prints the messages correctly and responds with the 200 success code; however, although the server successfully receives the message from the client, the server cannot send the message to pusher. Using the debug console on the pusher website confirms this.
This is triggering the error, so I believe it is the issue:
pusher.trigger('messages', 'new_message', message, function(err){
console.log("bananas");
done(err);
});
Server output when I catch the error from pusher.trigger
I have been trying to figure out whats wrong for a very long time now,
any help would be appreciated. I may try to get in touch with the pusher folks as I have followed the tutorial religiously and triple checked all my steps. There isn't a lot of resources for my particular issue but I have hope others with this issue can find this!
Thanks
The code looks correct. Can you please double check if there's a cluster value associated with it - you should be able to see it on dashboard.pusher.com.

REST request using Volley sends back a result that is different from Postman

I am using the GenericRequest (an extension of the built-in jsonrequest) to make a REST call to a server that takes in a json object and returns a string, which is "0" if the json object already exists and a nonzero string otherwise.
However, with the following code, I always get a "0" back no matter what I sent.
JSONObject userobj = new JSONObject();
try {
userobj.put("email",email);
userobj.put("password",password);
userobj.put("username",name);
} catch (JSONException e) {
e.printStackTrace();
}
Log.d(TAG, userobj.toString());
GenericRequest jsonObjReq = new GenericRequest(Request.Method.POST, REGISTER_URL, String.class, userobj,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// Handle access token.
Log.d(TAG, "Register received: " + response);
long token = Long.parseLong(response);
if(token == 0) {
Log.d(TAG, "Received 0!");
Toast.makeText(MainActivity.this, R.string.registerfail_toast, Toast.LENGTH_LONG).show();
} else {
Log.d(TAG, "Register success!");
Toast.makeText(MainActivity.this, R.string.Welcome, Toast.LENGTH_LONG).show();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show();
}
}) {
#Override
public String getBodyContentType() {
return "application/json";
}
};
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(0, -1, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
helper.add(jsonObjReq);
When testing in Postman, given the input like:
{
"email": "dlee23122",
"password": "1234",
"username": "dlee23122"
},
it gives back a nonzero string. (Screenshot as follows.) But when given a slightly different input using the Volley, it keeps giving back a "0". What could be the reason?
Thanks in advance!
Even i faced the same problem, volley default connection timeout is set to 5 sec and it was posting two times, so check for time in postman on right side, if it is closer to 5000ms or greater this might be the problem.
My problem got solved by adding the following to Volley request:
DefaultRetryPolicy retryPolicy = new DefaultRetryPolicy(0, -1, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsonObjectRequest.setRetryPolicy(retryPolicy);
if you want set custom retry policy look at this post Change Volley timeout duration

NodeMCU server bad response, when sending GET request from Android app

I've made little server based on NodeMCU. All works good, when I'm conneting from browser, but problem starts, when I'm trying to connect from Android app uisng OkHttp or Volley, I'm receiving exceptions.
java.io.IOException: unexpected end of stream on Connection using OkHttp,
EOFException using Volley.
Problem is very similar for this
EOFException after server responds, but answer didn't found.
ESP server code
srv:listen(80, function(conn)
conn:on("receive", function(conn,payload)
print(payload)
conn:send("<h1> Hello, NodeMCU.</h1>")
end)
conn:on("sent", function(conn) conn:close() end)
end)
Android code
final RequestQueue queue = Volley.newRequestQueue(this);
final String url = "http://10.42.0.17:80";
final StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
mTemperatureTextView.setText(response.substring(0, 20));
System.out.println(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
System.out.println("Error + " + error.toString());
mTemperatureTextView.setText("That didn't work!");
}
}
);
mUpdateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
queue.add(request);
}
});
What you're sending back is not HTTP. It's nothing but a protocol-agnostic HTML fragment. Furthermore, there's a memory leak lingering.
Try this instead:
srv:listen(80, function(conn)
conn:on("receive", function(sck,payload)
print(payload)
sck:send("HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n<h1> Hello, NodeMCU.</h1>")
end)
conn:on("sent", function(sck) sck:close() end)
end)
you need to send back some HTTP headers, HTTP/1.0 200 OK and the newlines are mandatory
each function needs to use it's own copy of the passed socket instance, see how I renamed conn to sck in the two callback functions, see https://stackoverflow.com/a/37379426/131929 for details
For a more complete send example look at net.socket:send() in the docs. That becomes relevant once you start sending more than just a couple of bytes.

SignalR HTTP status 400 multiple clients

I'm running an application with SignalR 2.2.0 on server side and signalr-java-client (self compiled, last GitHub version) on Android as client.
Currently, there are 4 clients connected to my hub. From time to time, it happens, that all 4 clients simultaneously receive the HTTP status 400 with the message "The connection id is in the incorrect format" (the clients were connected before). I analyzed this multiple times and am not able to find any information/pattern when or why this happens.
The connecten is secured via JWT, the token is definitely valid. When retrieving a new token, the connection is stopped and started again. Apart from this, it is very unlikely that the error is device-related, because the error is thrown at all 4 clients the same time.
I know, this error can occur when the client's Identity changes, but an Identity change for 4 clients the same time seems very unlikely to me.
This is the server-code used for authentication (Deepak asked).
The following method gets called in my Startup.cs:
public static void ConfigureOAuth(IAppBuilder app, string audienceID, string sharedSecret)
{
byte[] secret = TextEncodings.Base64Url.Decode(sharedSecret);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
Provider = new MyOAuthBearerAuthenticationProvider(),
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceID },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(Issuer, secret)
}
});
}
Here's the code of MyOAuthBearerAuthenticationProvider class:
class MyOAuthBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
/// <summary>
/// Get's a JWT from querysting and puts it to context
/// </summary>
public override Task RequestToken(OAuthRequestTokenContext context)
{
if (context.Token == null)
{
string value = context.Request.Query.Get("auth_token");
if (!string.IsNullOrEmpty(value)) //token from queryString
{
context.Token = value;
}
}
return Task.FromResult<object>(null);
}
}
I have to retrieve the token from query string, because additionally to the java-client, a javascript client is used, which is not able to set headers.
Lastly, I secure my hub and some of it's methods with the Authorization attribute:
[Authorize(Roles = "MyExampleRole")]
This is the client-code for connection:
public boolean connect(String url, String token) {
if (connected) {
return true;
}
try {
this.hubConnection = new HubConnection(url, "auth_token=" + token, true, logger);
this.hubProxy = hubConnection.createHubProxy("MyHub");
this.hubProxy.subscribe(this.signalRMethodProvider);
this.hubConnection.stateChanged(stateChangedCallback);
SignalRFuture<Void> awaitConnection = this.hubConnection.start();
awaitConnection.get(10000, TimeUnit.MILLISECONDS);
return true;
}
catch (InterruptedException | TimeoutException | ExecutionException e) {
log.error("connect", e);
return false;
}
}
Does anybody have an Idea, how to fix this problem or where I may receive further information?
Thank you very much
-Lukas
seems fine...
possible alteration you can do is change
awaitConnection.get(10000, TimeUnit.MILLISECONDS);
to
awaitConnection.done(new Action<Void>() {
#Override
public void run(Void obj) throws Exception {
Log.d(TAG, "Hub Connected");
}
}).onError(new ErrorCallback() {
#Override
public void onError(Throwable error) {
error.printStackTrace();
Log.d(TAG, "SignalRServiceHub Cancelled");
}
}).onCancelled(new Runnable() {
#Override
public void run() {
Log.d(TAG, "SignalRServiceHub Cancelled");
}
});

Trying to get a json response from API web to Android with android-async-http lib

I've been working with android-async-http (http://loopj.com/android-async-http/) lib with android but for some reason I can't catch the response from server, I know that the server receive and do the things that should do, but I can't get the response for no reason.
Here is the method that calls the API:
public User registUser(String mail, String pass) throws UnsupportedEncodingException {
final User user = new User();
user.setToken("enter");
String bodyAsJson = "{\"user\":{\"email\":\""+mail+"\",\"password\":\""+pass+"\"}}";
StringEntity entity = new StringEntity(bodyAsJson);
Header[] headers = {
new BasicHeader("Content-type", "application/json")
};
client.post(this.context, "http://104.131.189.224/api/user", headers , entity, "application/json", new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject json) {
try {
json = json.getJSONObject("user");
user.setId(json.getInt("id"));
user.setEmail(json.getString("email"));
user.setPassword("123456");
user.setToken(json.getString("auth_token"));
} catch ( JSONException e) {
user.setToken("not json");
} catch (Exception e) {
user.setToken("error ");
}
}
#Override
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
user.setToken("comes json array");
}
#Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
user.setToken(responseString);
}
#Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
user.setToken("error");
}
#Override
public void onRetry(int retryNo) {
user.setToken("nothing");
}
});
return user;
}
when I call the method, the user.getToken shows only the "enter" that I put in the beginin, that means, never entered in the onSuccess, onFailure, or onRetry methods.
but I know that the server receive my request because the server log shows:
(example: email: carlos#prueba.com, pass: prueba)
"=>"carlos#prueba.com", "password"=>"[FILTERED]"}}
D, [2015-03-17T05:15:27.660562 #28450] DEBUG -- : (0.8ms) BEGIN
D, [2015-03-17T05:15:27.671126 #28450] DEBUG -- : User Exists (2.6ms) SELECT
1 AS one FROM `users` WHERE `users`.`email` = BINARY 'carlos#prueba.com' LIMIT
1
D, [2015-03-17T05:15:27.677448 #28450] DEBUG -- : SQL (1.0ms) INSERT INTO `us
ers` (`email`, `encrypted_password`, `created_at`, `updated_at`) VALUES ('carlos
#prueba.com', '$2a$10$Dg358IzoaG5KVJ8ZJTeViev2v5B9CAnAqIYI1Zd4EIFC.0Mh.nMU6', '2
015-03-17 05:15:27.672898', '2015-03-17 05:15:27.672898')
D, [2015-03-17T05:15:27.681514 #28450] DEBUG -- : (2.0ms) COMMIT
D, [2015-03-17T05:15:27.684634 #28450] DEBUG -- : User Exists (0.6ms) SELECT
1 AS one FROM `users` WHERE `users`.`auth_token` = '6aff3b4162cfcf3062a6db12a1c
ee2bc' LIMIT 1
D, [2015-03-17T05:15:27.685582 #28450] DEBUG -- : (0.2ms) BEGIN
D, [2015-03-17T05:15:27.690901 #28450] DEBUG -- : SQL (0.8ms) UPDATE `users`
SET `auth_token` = '6aff3b4162cfcf3062a6db12a1cee2bc', `updated_at` = '2015-03-1
7 05:15:27.687516' WHERE `users`.`id` = 11
D, [2015-03-17T05:15:27.693809 #28450] DEBUG -- : (1.8ms) COMMIT
I, [2015-03-17T05:15:27.698987 #28450] INFO -- : Rendered api/users/_user.jso
n.jbuilder (0.3ms)
I, [2015-03-17T05:15:27.700292 #28450] INFO -- : Rendered api/users/create.js
on.jbuilder (3.2ms)
I, [2015-03-17T05:15:27.701395 #28450] INFO -- : Completed 200 OK in 223ms (Vie
ws: 6.3ms | ActiveRecord: 10.0ms)
the server should response a json in the format:
{"user":{"id":3,"email":"carlos#prueba.com","auth_token":"dc45800fddee07cf9b300d2765283cb2"}}
Most tutorials are outdated and try to use the apache library however I did find a working one.
While trying to manipulate a Team Treehouse Tutorial ( https://teamtreehouse.com/library/build-a-weather-app/ ) to work for an event finder api instead of their weather api, I ran into the same problem.
They use the OkHttp library
compile the OkHttp library into your build.gradle file under Module:app
compile 'com.squareup.okhttp:okhttp:2.4.0'
this is the most recent version as of 7/9/15
visit the api url to see if you are indeed retrieving the data and that it is in the correct format. For me I was using Eventful.com and finding events in my area.
( https://api.eventful.com/json/events/search?l=arizona&within=15&units=miles&app_key= ) where my app key would go after the "=" at the end of the url.
at this point everything was working great, now I needed to add the OkHttp code to download the json data
add internet permissions to your manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.thestephenmiller.myApp" >
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
Open the class or activity where the request will be made and add the following code.
String eventfulUrl = "https://api.eventful.com/json/events/search?l=arizona&within=15&units=miles&app_key="+apiKey;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(eventfulUrl)
.build();
and make the call
`
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
}
#Override
public void onResponse(Response response) throws IOException {
try {
if (response.isSuccessful()) {
Log.v(TAG, response.body().string());
}
} catch (IOException e) {
Log.e(TAG, "Exception Caught: ", e);
}
}
});
all of which was added to onCreate()
this line
Log.v(TAG, response.body().string());
is what outputs the response to the log and is in the if statement where you can deal with the data.
The code will not get the response if the url does not start with "http://" or "https://" however the app will still run error free

Categories

Resources