My code in MVC API C# this:
[System.Web.Http.HttpPost]
[System.Web.Http.Route("api/ServiceV1/Test")]
public IHttpActionResult Test()
{
return BadRequest("Catch this message in Android application");
}
Result in PostMan
{
"Message": "Catch this message in Android application"
}
I catch this error message on android application. I used okhttp3.
String MIME_JSON = "application/json";
Gson gson = new Gson();
RequestBody body = RequestBody.create(MediaType.parse(MIME_JSON), gson.toJson(object));
Request request = new Request.Builder()
.url(baseUrl + route)
.post(body)
.build();
okHttpClient.newCall(request).execute();
How do to catch this message on the Android application?
the Call.execute() method returns a Response object, which has the code() method to find the status code, and the isSuccessful() method to find whether the code is in the [200,300) range, which generally represent success.
Generally looking at the documentation of libraries you are working with is helpful. Here's the documentation for okhttp
Can read this error:
Response response=okHttpClient.newCall(request).execute();
if (response.code() != 200) {
errorMessage= response.body().string();
}
Related
I want to a upload file on my server and I've decided to try OKHTTP instead of my current method which is based on android own HTTP implementation and AsyncTask.
Anyway, I used OKHTTP and its asynchronous implementation (from its own recipes) but it returns an empty message (the request code is ok, the message is empty) in both GET and POST methods.
Did I implement it wrong or is there anything else remained that I did not considered? In the meantime, I couldn't find a similar case except this which says used AsyncTask.
Here's the code:
Request request;
Response response;
private final OkHttpClient client = new OkHttpClient();
private static final String postman_url = "https://postman-echo.com/get?foo1=bar1&foo2=bar2";
String message_body;
public void Get_Synchronous() throws IOException
{
request = new Request.Builder()
.url(postman_url)
.build();
Call call = client.newCall(request);
response = call.execute();
message_body = response.toString();
//assertThat(response.code(), equalTo(200));
}
public void Get_Asynchronous()
{
request = new Request.Builder()
.url(postman_url)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException
{
message_body = response.toString();
}
public void onFailure(Call call, IOException e)
{
}
});
}
Edit:
I catch the log on response:
onResponse: Response{protocol=h2, code=200, message=, url=https://postman-echo.com/get?foo1=bar1&foo2=bar2}
OK, for anyone who wants to receive an string from a call, response and response.messgage() don't provide that. To catch the response from your provider, you just need to call response.body().string() inside onResponse which returns the message inside your request.
But after all, Retrofit is a better choice if you want to receive a JSON file using
.addConverterFactory(GsonConverterFactory.create(gson)).
If you still want to receive an string just use .addConverterFactory(ScalarsConverterFactory.create()) as explained here.
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.
I've answered my own question here. See the first code block where I use php://input to get the posted data.
I'm trying to send a post request from my app to a webserver, and I'm checking for the post vars using PHP:
if( isset( $_POST['name'] ) ){
echo json_encode(['status' => 1]);
}else if( $posted_data = (string) #file_get_contents('php://input') ) {
echo json_encode(['status' => 2]);
}else{
echo json_encode($_POST);
}
The request always returns and empty json encoded array.
I'm using the latest Android Studio, and the latest OkHttp, com.squareup.okhttp3:okhttp:3.4.1. For me, this is like the "Hello World" of OkHttp in Android Studio.
In MainActivity.java:
public void postSomething(View view) {
String url = "https://example.com/json_api_test.php";
String json = "{\"name\":\"cholula\"}";
OkHttpPostHandler handler = new OkHttpPostHandler();
String result = "";
try {
result = handler.execute(url, json).get();
} catch (Exception e) {
e.printStackTrace();
}
displayPostResponse(result + "\n");
}
My OkHttpPostHandler.java:
public class OkHttpPostHandler extends AsyncTask<String, Void, String> {
OkHttpClient client = new OkHttpClient();
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
#Override
protected String doInBackground(String... params) {
RequestBody body = RequestBody.create(JSON, params[1]);
Request request = new Request.Builder()
.url(params[0])
.post(body)
.build();
try {
Response response = client.newCall(request).execute();
return response.body().string();
}
catch( Exception e ){
return "HTTP Request Error";
}
}
}
When I debug, I can see that the params[1] value is the expected json-like string, but that's the last time I see it.
I've tried forming the json in a number of ways, so I'm not sure if that's the problem. I just don't understand why I can't see the posted vars when the post request gets to the server.
How can I see the posted vars on the webserver? What am I doing wrong? I've only been using Android Studio and Java for less than a week, so I have no clue. I've really looked around the internet a lot for the answer, and so posting here is the last resort.
Thanks!
It turns out that one must use php://input to get the posted data.
I am working on a project in which I am making a call to one of my servers using RestTemplate which is running a restful service and getting the response back from them.
The response that I will be getting from my server can be either of these error responses (that's all I have for error response) if something has gone wrong -
{"warning": "user_id not found", "user_id": some_user_id}
{"error": "user_id for wrong partition", "user_id": some_user_id, "partition": some_partition}
{"error": "missing client id", "client_id":2000}
or below successful response (it can be any random json string key can also be different) -
{"#data": {"oo":"1205000384","p":"2047935"}
If I am getting any error response as mentioned above, then I am deserializing it (my bad :( ) so that I can log them as an error with a specific error or warning I got front the server which can be for example - user_id not found or missing client id.
If it is a successful response then also I am deserializing it which I don't need for my use case as we don't have any POJO and I just need to return the response as it is which I have got from the server.
In my use case, I don't need to deserialize my response string if it is a successful response as we don't have any POJO for that and we are returning the response string as it is which we have got from the server. But just for logging specific error messages (if I am getting error response from the server) I am deserializing it which I am thinking is unnecessary. There might be better solution for my use case.
Below is my Java client which is calling Callable task using future.get -
public class TestingClient implements IClient {
private ExecutorService service = Executors.newFixedThreadPool(10);
private RestTemplate restTemplate = new RestTemplate();
#Override
public String executeSync(ClientKey keys) {
String response = null;
try {
ClientTask ClientTask = new ClientTask(keys, restTemplate);
Future<String> future = service.submit(ClientTask);
response = handle.get(keys.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
} catch (Exception e) {
}
return response;
}
}
And now below is my ClientTask class which implements Callable interface. In the call method, I am generating an URL and then hit the server using RestTemplate and get the response back -
class ClientTask implements Callable<String> {
private ClientKey cKeys;
private RestTemplate restTemplate;
public ClientTask(ClientKey cKeys, RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.cKeys = cKeys;
}
#Override
public String call() throws Exception {
// .. some code here
String url = "some_url";
String response = restTemplate.getForObject(url, String.class);
String test = checkJSONResponse(response);
return test;
}
private String checkJSONResponse(final String response) throws Exception {
// may be there are some better way of doing it for my scenario instead of using GSON
Gson gson = new Gson();
String str = null;
JsonObject jsonObject = gson.fromJson(response, JsonObject.class); // parse it, may be performance issues here/
if (jsonObject.has("error") || jsonObject.has("warning")) {
final String error = jsonObject.get("error") != null ? jsonObject.get("error").getAsString() : jsonObject
.get("warning").getAsString();
// log specific `error` here using log4j
str = response;
} else {
str = response;
}
return str;
}
}
As you can see in my above code we are deserializing the JSON string only to log specific error messages if we are getting any error response back. But for successful response we don't need any deserialization but still we are doing it.
Is there any better way of solving this problem? Because currently I am seeing some performance issues with the GSON deserialization.
The only way I can identify successful response along with error response is with error or warning in the response so I am thinking of using regular expressions which can identify error or warning as the key in the response string. If they contain error or warning in the response string then extract the specific error or warning message and log it. But not sure whether this will have any performance benefit or not.
Is there any other better way of solving this problem without using GSON deserialization.
It is a good practice to use HTTP status codes for your responses (e.g. BAD_REQUEST, NOT_FOUND). Return one of them from the server and then check on the client. It will allow to parse response only if some error code is returned:
String result = restTemplate.execute("url", HttpMethod.GET, null, new HttpMessageConverterExtractor<String> {
#Override
public MyEntity extractData(ClientHttpResponse response)
throws IOException {
String result = super.extractData(response);
if (response.getStatusCode() != HttpStatus.OK) {
// parse message and log only for some error code
JsonObject errorJson = parse(result);
log.warn("Got {} status error, with message [{}]", response.getStatusCode(), errorJson.get("warning"));
}
return result;
}
});
You do not need to deserialize to a POJO.
A simple JSON parser such as the one found on json.org will provide minimal JSON parsing an return a JSONObject that you can query.
I very much doubt that
you can come up with a faster parsing of your json responses using regular expressions or otherwise, without taking the risk of failing in corner cases
given the size of your response strings, that the JSON parsing is the performance bottleneck in your code
Unless you have done some serious profiling, I would play safe and follow the first rule of program optimization
I am working on a project in which I am making a call to one of my servers using RestTemplate which is running a restful service and getting the response back from them.
The response that I will be getting from my server can be either of these error responses (that's all I have for error response) if something has gone wrong -
{"warning": "user_id not found", "user_id": some_user_id}
{"error": "user_id for wrong partition", "user_id": some_user_id, "partition": some_partition}
{"error": "missing client id", "client_id":2000}
or below successful response (it can be any random json string key can also be different) -
{"#data": {"oo":"1205000384","p":"2047935"}
If I am getting any error response as mentioned above, then I am deserializing it (my bad :( ) so that I can log them as an error with a specific error or warning I got front the server which can be for example - user_id not found or missing client id.
If it is a successful response then also I am deserializing it which I don't need for my use case as we don't have any POJO and I just need to return the response as it is which I have got from the server.
In my use case, I don't need to deserialize my response string if it is a successful response as we don't have any POJO for that and we are returning the response string as it is which we have got from the server. But just for logging specific error messages (if I am getting error response from the server) I am deserializing it which I am thinking is unnecessary. There might be better solution for my use case.
Below is my Java client which is calling Callable task using future.get -
public class TestingClient implements IClient {
private ExecutorService service = Executors.newFixedThreadPool(10);
private RestTemplate restTemplate = new RestTemplate();
#Override
public String executeSync(ClientKey keys) {
String response = null;
try {
ClientTask ClientTask = new ClientTask(keys, restTemplate);
Future<String> future = service.submit(ClientTask);
response = handle.get(keys.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
} catch (Exception e) {
}
return response;
}
}
And now below is my ClientTask class which implements Callable interface. In the call method, I am generating an URL and then hit the server using RestTemplate and get the response back -
class ClientTask implements Callable<String> {
private ClientKey cKeys;
private RestTemplate restTemplate;
public ClientTask(ClientKey cKeys, RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.cKeys = cKeys;
}
#Override
public String call() throws Exception {
// .. some code here
String url = "some_url";
String response = restTemplate.getForObject(url, String.class);
String test = checkJSONResponse(response);
return test;
}
private String checkJSONResponse(final String response) throws Exception {
// may be there are some better way of doing it for my scenario instead of using GSON
Gson gson = new Gson();
String str = null;
JsonObject jsonObject = gson.fromJson(response, JsonObject.class); // parse it, may be performance issues here/
if (jsonObject.has("error") || jsonObject.has("warning")) {
final String error = jsonObject.get("error") != null ? jsonObject.get("error").getAsString() : jsonObject
.get("warning").getAsString();
// log specific `error` here using log4j
str = response;
} else {
str = response;
}
return str;
}
}
As you can see in my above code we are deserializing the JSON string only to log specific error messages if we are getting any error response back. But for successful response we don't need any deserialization but still we are doing it.
Is there any better way of solving this problem? Because currently I am seeing some performance issues with the GSON deserialization.
The only way I can identify successful response along with error response is with error or warning in the response so I am thinking of using regular expressions which can identify error or warning as the key in the response string. If they contain error or warning in the response string then extract the specific error or warning message and log it.
I guess there might be some better way of solving this problem without paying the cost for deserialization.
Just relying on a regex is I think to dangerous. What if the server slightly changes the output format?
I would try to make a quick test, possibly with a simple regexp looking for the string "error" and if there is a chance that it is an error response do a full deserialization to determine if it really was an error or not.
You would pay the extra cost only for false positives when a regular response by chance triggers the quick check.
I would use http codes to control success/fail data parsing.