I am trying to send a POST request with a JSON BODY in CodeName one.
It reaches the server with an empty Json String.
Here is the code that makes the connection and sends the message:
JSONObject json = new JSONObject();
class MyConnection extends ConnectionRequest {
public Map<String, Object> results;
#Override
protected void readResponse(InputStream input) throws IOException {
JSONParser jp = new JSONParser();
results = jp.parseJSON(new InputStreamReader(input, "UTF-8"));
}
#Override
protected void handleErrorResponseCode(int code, String message) {
showError("The server returned the error code: " + code);
}
#Override
protected void handleException(Exception err) {
showError("There was a connection error: " + err);
}
#Override
protected void postResponse() {
try {
json.put("AAA", "AAA");
json.put("BBB", "BBB");
json.put("CCC", "CCC");
} catch (JSONException ex) {
ex.printStackTrace();
}
}
#Override
protected void buildRequestBody(OutputStream os) throws IOException {
os.write(json.toString().getBytes("UTF-8"));
}
}
---
MyConnection connec = new MyConnection ();
connec.setUrl("http://testServoce/addUser");
connec.setPost(true);
connec.setContentType("application/json");
InfiniteProgress prog = new InfiniteProgress();
Dialog dlg = prog.showInifiniteBlocking();
connec.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueueAndWait(connec);
I'm not sure what your intention was but it looks like you misunderstood the goal of the postResponse method. It's unrelated to the POST web method and is just called after the response completed on the EDT. So changing the JSON value there is irrelevant.
Also it looks like you are using two separate JSON parsers for some reason. The builtin one and the org.json one from one of the cn1libs.
Related
Program must accept requests to add and remove tasks from the list through the server. After starting, server accepts connections in an infinite loop and reads from them a line containing json of the form:
{ "type": "ADD", "task": "Название задачи" }
where type is the type of operation (ADD or REMOVE) and task is the task itself. After processing the request, a list of all tasks should be displayed in the console. After connecting, my console gives null. What can be wrong?
Server class:
public class TodoServer {
public TodoServer(int port, Todos todos) {
while (true) {
try (ServerSocket serverSocket = new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
System.out.println("New connection accepted");
final String json = in.readLine();
Gson gson = new Gson();
String type = gson.fromJson("\"type\"", String.class);
String task = gson.fromJson("\"task\"", String.class);
if (type.equals("ADD")) {
todos.addTask(task);
} else if (type.equals("REMOVE")) {
todos.removeTask(task);
}
System.out.println(todos.getAllTasks());
} catch (IOException e) {
System.out.println("Соединение разорвано");
}
}
}
public void start() throws IOException {
int port = 8989;
System.out.println("Starting server at " + port + "...");
}
}
Task class:
public class Todos {
static ArrayList <String> tasks = new ArrayList<>();
public void addTask(String task) {
tasks.add(task);
Collections.sort(tasks);
}
public void removeTask(String task) {
tasks.remove(task);//...
}
public String getAllTasks() {
return tasks.toString();
}
public ArrayList<String> getListTask() {
return tasks;
}
}
The Main class which the server starts:
public class Main {
public static void main(String[] args) throws IOException {
Todos todos = new Todos();
TodoServer server = new TodoServer(8989, todos);
server.start();
}
}
From what you've shown here, your parsing and use of JSON is the issue. As a starting point, you read a String json but then do nothing with it.
You'll want to parse that value into an object, and then access values out of it (like you would a dictionary or map). How to do that with GSON should have plenty of documentation and examples readily available.
If you are using an IDE for development, I also recommend using this as a great opportunity for trying the debugger out - setting breakpoints, inspecting values, etc!
It would be better to define a simple POJO to represent a task:
#Data
class MyTask {
private String type;
private String task;
}
Here #Data is a Lombok annotation which provides the boilerplate code of getters/setters/default constructor/toString/hashCode/equals.
Then the instance of such POJO is deserialized from JSON abd processed as needed:
final String json = in.readLine();
MyTask task = new Gson().fromJson(json, MyTask.class);
if ("ADD".equals(task.getType())) {
todos.addTask(task.getTask());
} else if ("REMOVE".equals(task.getType())) {
todos.removeTask(task.getTask());
}
System.out.println(todos.getAllTasks());
I am sending API requests to a backend API using Spring in Android (Java). My question is how to receive validation errors to the error handler at ex 400 bad request response. Here is my code:
class RestTask extends AsyncTask<String,Void,ResponseEntity<ExpectedReturn>>
{
protected ResponseEntity<ExpectedReturn> doInBackground(String... uri)
{
try{
final String url = uri[0];
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(subscriber.getErrorHandler());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// set authentication tokens:
ResponseEntity<ExpectedReturn> response = restTemplate.exchange(url,callMethod,httpEntity, expectedReturnClass);
return response;
}catch(Exception e)
{
System.out.println(e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(ResponseEntity<ExpectedReturn> result) {
if(result !=null && result.getBody() !=null)
{
subscriber.getSubscriber().onComplete(result.getBody(),result.getStatusCode());
}
}
}
My question is, if the post data fails validation (is incorrect), the API will return as JSON error object with errors, ex:
In case of a validation error, the error handler is called with a ClientHttpResponse object as a parameter. Calling the response.getBody() returns an InputStream. My question is, is there any way of receiving an object mapped from the JSON error response (as shown above) to the error handler, or perhaps converting the input stream to something readable (like a hashmap) so I can display the errors returned by the API (ex: "Name is required" etc...)?
I've tested your code and in case of a 400 bad request the catch block receives an instance of HttpClientErrorException which has a method to get the error body as String:
private class HttpRequestTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
try {
final String url = "https://reqres.in/api/login";
RestTemplate restTemplate = new RestTemplate();
//Same result with restTemplate.exchange() too
return restTemplate.postForObject(url, "{\n" +
" \"email\": \"peter#klaven\"\n" +
"}", String.class);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
if (e instanceof HttpClientErrorException) {
String responseBodyAsString = ((HttpClientErrorException) e).getResponseBodyAsString();
Log.e(TAG, "Validation error" + responseBodyAsString);
//You can parse this with gson or jackson here
return responseBodyAsString;
}
}
return null;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "onPostExecute() called with: result = [" + result + "]");
}
}
Which prints in:
W/RestTemplate: POST request for "https://reqres.in/api/login" resulted in
400 (Bad Request); invoking error handler
E/MainActivity: 400 Bad Request
E/MainActivity: Validation error{"error":"Missing email or username"}
D/MainActivity: onPostExecute() called with: result = [{"error":"Missing email or username"}]
If you want to use the none default error handler and set your custom error handler you can get the error message as string this way:
restTemplate.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return response.getStatusCode().is4xxClientError();
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
String errorResponse = new String(getResponseBody(response), getCharset(response).name());
Log.e(TAG, "handleError: called with: " + errorResponse);
}
});
private byte[] getResponseBody(ClientHttpResponse response) {
try {
InputStream responseBody = response.getBody();
if (responseBody != null) {
return FileCopyUtils.copyToByteArray(responseBody);
}
} catch (IOException ex) {
// ignore
}
return new byte[0];
}
private Charset getCharset(ClientHttpResponse response) {
HttpHeaders headers = response.getHeaders();
MediaType contentType = headers.getContentType();
return contentType != null ? contentType.getCharSet() : Charset.defaultCharset();
}
Then you can use Jackson or Gson to parse the error response as below:
new Gson().fromJson(responseBodyAsString, ExpectedResponse.class);
Note I've just did the same thing as implemented in DefaultResponseErrorHandler
Edit:
The whole AsyncTask and Spring Android APIs are so outdated, Here is the same example with Retrofit:
api.login(new BodyModel("peter#klaven"))
.enqueue(new Callback<ExpectedModel>() {
#Override
public void onResponse(#NonNull Call<ExpectedModel> call, #NonNull Response<ExpectedModel> response) {
if (response.isSuccessful()) {
//Do what you got to do
} else {
Converter<ResponseBody, ErrorModel> converter = MainActivity.this.retrofit.responseBodyConverter(ErrorModel.class, new Annotation[0]);
ErrorModel errorModel = null;
try {
errorModel = converter.convert(response.errorBody());
Toast.makeText(MainActivity.this, errorModel.toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(#NonNull Call<ExpectedModel> call, #NonNull Throwable t) {
t.printStackTrace();
}
})
You can find the full gist in my github repo
Newbie here.
I'm working on a simple client-server Android app with my friend, trying my best to write the cleanest most beautiful code that I can, strictly following the SOLID principles and using Design Patterns to decouple my classes and components. I wrote this method, hoping that it'll be the only networking code I'll have to write:
private void VolleyJSONPostParserRequest(String url, JSONObject requestBody,
final Handler handler, final IParser replyParser) {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(Request.Method.POST, url, requestBody, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
Object parsedResponse = replyParser.parse(response.toString());
notifyObservers(handler, parsedResponse);
} catch (Exception e) {
Log.e(TAG,handler.getClassName() + " reply parsing error!");
notifyObservers(handler, null); }
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG,"Error receiving response for " + handler.getClassName());
notifyObservers(handler, null);
}
});
}
To my discontent, I found out that sometimes the server's reply will be a JSONObject, and sometimes it will be a JSONArray. I do not want to copy-paste the entire thing and replace "JsonObjectRequest" with "JsonArrayRequest". What's a good solution for this problem? Or is it one of those cases where I'd be better off just copying and pasting?
In my opinion, you should implement a custom request (for example, public class CustomRequest extends Request<NetworkResponse>). Please read the following Google's training doc for more information:
Google Volley - Implementing a Custom Request
then...
#Override
public void onResponse(NetworkResponse response) {
try {
final String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
// Check if it is JSONObject or JSONArray
Object json = new JSONTokener(jsonString).nextValue();
if (json instanceof JSONObject) {
//do something...
} else if (json instanceof JSONArray) {
//do something...
} else {
//do something...
}
...
} catch (UnsupportedEncodingException | JSONException e) {
e.printStackTrace();
}
}
I have a MainActivity in which I instantiate a class. This class contains basic data and two important methods: GetRequestAccessUrl(params) and getToken(string) which returns an AuthResponse.
The first method runs fine, the string is generated and processed in the application. However, the getToken-method involves networking and is therefore prohibited to run on the main thread and AsyncTask is recommended. The implementation of the latter method is as following:
public AuthResponse getToken(String code) {
if (secrete == null) {
throw new IllegalStateException("Application secrete is not set");
}
try {
URI uri = new URI(TOKEN_URL);
URL url = uri.toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
try {
StringBuilder sb = new StringBuilder();
sb.append("client_id=" + clientId);
sb.append("&client_secret=" + secrete);
sb.append("&code=" + code);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(sb.toString().getBytes("UTF-8"));
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ conn.getResponseCode());
}
Reader br = new InputStreamReader((conn.getInputStream()));
Gson gson = new Gson();
return gson.fromJson(br, AuthResponse.class);
} finally {
conn.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
In the MainActivity the entire class is created, the first method is called, some actions are executed and the the getToken-method should run. However I seem to be completely stuck on how to do this, or how to create a (working) AsyncTask regarding this method. Any help is appreciated.
new YourAsyncTask ().execute(code);
private class YourAsyncTask extends AsyncTask<String, Integer, Integer> {
protected Long doInBackground(String... codes) {
AuthResponse res = getToken(codes[0]);
doSthWithRes(res);
}
protected void onProgressUpdate(Integer... progress) {}
protected void onPostExecute(Integer result) {}
}
This will probably work. Depending on what you wanna do with the AuthResponse.
As you can see the ASyncTask is more of a batch processing in the background. I prefer just using a Standard Thread. Additionally you may want to process the AuthResponse in the UIThread.
Here the Quick and dirty version:
/* It would be better to create a subclass of Runnable and pass the Code in the constructor*/
final String code = "testcode";
//Create the new Thread
Thread t = new Thread(new Runnable() {
#Override
public void run() {
final AuthResponse res = getToken(code);
//RunOnUiThread is a method of the Activity
runOnUiThread(new Runnable() {
#Override
public void run() {
doSomethingWithResponse(res);
}
});
}
});
t.start()
Try something like this
new AsyncTask<String, void, AuthResponse>() {
#Override
protected String doInBackground(String... params) {
String id = params[0];
String secret = params[1];
String code = params[2];
//do your stuff
return myAuthResponse;
}
#Override
protected void onPostExecute(AuthReponse result) {
//do stuff with AuthResponse
}
}.execute(clientId, clientSecret, code);
In onPostExecute you can handle the AuthResponse on the UIThread.
I think that I answered this in the following thread :
Java AsyncTask passing variable to main thread
A Http request is done in background in an Asyntask and the result is sent to the main activity thanks to a Callback. I gave a sample code on the answer.
currently I'm doing something for my project wherein I created a separate Class that will only handle the Asynctask and get the value of the webservices I passed and that class should return the JSON response as a String. Now I already achieved it using taskName.execute().get(); wherein it will wait for the task to complete but the problem is it is also waiting for the task to complete before displaying the screen layout. Making my progressDialog useless and cause a delay on switching screens. Here's my code for now:
For the Class with AsyncTask:
public class UtilGetResponse {
Context context;
Map hash_values = new HashMap();
int DialogType;
String response;
/*
PLAN FOR DialogTypes:
* 0 - Standard Please wait dialog
* 1 - Progress dialog
* 2 - Camera upload dialog
* */
InputStream is = null;
StringBuilder string_builder = null;
public UtilGetResponse(Map values, Context baseContext, int type){
/*initialize class and pass the hash values for parameters*/
context = baseContext;
hash_values.putAll(values);
DialogType = type;
}
public String startTask(){
//TODO CASE WHEN BASED ON THE DIALOG TYPE SPECIFIED
Utilities util = new Utilities();
if(util.isOnline(context)){
try {
new UploaderTaskStandard().execute().get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return response; //THE RESPONSE ONLY SHOW ONCE THE WHOLE TASK IS COMPLETED
}
public class UploaderTaskStandard extends AsyncTask<Map, Void, Void> {
ProgressDialog simpleDialog;
#Override
protected void onPreExecute() {
/*Do something before the async task starts*/
simpleDialog = new ProgressDialog(context);
simpleDialog.setMessage("Please wait");
simpleDialog.show();
}
#Override
protected Void doInBackground(Map... maps) {
uploadData();
return null;
}
protected void onPostExecute(Void v) {
/*Do something after the task is complete*/
simpleDialog.dismiss();
}
}
private void uploadData() {
response = "null";
String url = hash_values.get("url").toString().replace(" ", "%20"); //get the URL replacing the space with %20
//If the user is trying to upload a file use this part
try {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
MultipartEntity mpEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
/*This will convert the hashMap sent into individual part per key per value*/
Set set = hash_values.entrySet();
Iterator iterator = set.iterator();
/*do a loop passing all the data on a string*/
while(iterator.hasNext()) {
Map.Entry mapEntry = (Map.Entry)iterator.next();
String keyword = String.valueOf(mapEntry.getKey());
String value = String.valueOf(mapEntry.getValue());
/*this will check if the passed data is a URL, file or a simple value*/
if(!keyword.equals("url")){
if(value.matches("(.*)/(.*)")){
File file = new File(value);
Log.v("Does this exists?",String.valueOf(file.exists()));
if(file.exists()){
FileBody upload_file;
upload_file = new FileBody(file);
/*not url but file*/
mpEntity.addPart(keyword, upload_file);
}else{
/*not url and not file*/
mpEntity.addPart(keyword, new StringBody(value));
}
}else{
/*not URL and not file*/
mpEntity.addPart(keyword, new StringBody(value));
}
}
}
post.setEntity(mpEntity);
HttpResponse response = client.execute(post);
HttpEntity resEntity = response.getEntity();
is = resEntity.getContent();
} catch (Exception e) {
e.printStackTrace();
response = "null";
}
/*convert JSON to string*/
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
string_builder = new StringBuilder();
String line = "0";
while ((line = reader.readLine()) != null) {
string_builder.append(line + "\n");
}
is.close();
response = string_builder.toString();
}catch(Exception e){
e.printStackTrace();
}
}
}
And to call this:
Map hash_values = new HashMap();
try{
HashMap params = new HashMap<String,String>();
params.put("param1", "YOUR_PARAM");
params.put("url", "YOUR_WEBSERVICE_URL");
//pass parameters
hash_values.putAll(params);
//start async task
UtilGetResponse util = new UtilGetResponse(hash_values, getActivity(), 0);
String result = util.startTask();
Log.v("The result string",result);
}catch (Exception e){
e.printStackTrace();
e.getCause();
Toast.makeText(getActivity(), "Oops problem", Toast.LENGTH_SHORT).show();
}
Is there's a way for me to do this properly without really waiting for the whole task to finish before moving to the next screen? I'm thinking of using a Handler but I'm not really familiar on how to use it anyway.
Your issue is with usage of this
new UploaderTaskStandard().execute().get();
Although you using AsynTask, but still making system wait until result which is against your requirement, what you need is a delivery mechanism, which will notify you back once results are ready. You can take either of two approaches.
change to this, and implement one of below mechanism.
new UploaderTaskStandard().execute();
Implementing handler, and posting result back once result available.
Implementing observer design pattern, where you create an interface with methods such as onResultReady, and passing an object of class implementing above interface to your method startTask, and posting result back from AsyncTask onPostExecute once it is available via interface mechanism.
Going via interface will be very easy and in this way your code will be independent of your network logic, sample code below
// Observer listener interface design
interface ResultListener{
// You can overload this method with data type you want to return
public void onResultReceived();
// Use them in a proper way for sending error message back to your program
public void onTaskCancelled();
public void onError();
}
// This will be your new method signature
public String startTask(ResultListener listener){
// Call it liske this, passing listener reference
new UploaderTaskStandard().execute(listener);
}
// This is your AsyncTask model
public class UploaderTaskStandard extends AsyncTask<ResultListener, Void, Void> {
ResultListener listener;
#Override
protected Void doInBackground(ResultListener... maps) {
this.listener = maps[0];
uploadData();
return null;
}
protected void onPostExecute(Void v) {
/*Do something after the task is complete*/
simpleDialog.dismiss();
// Notify back to calling program
listener.onResultReceived();
}
}