Saving incoming HTTP Post request content [duplicate] - java

I'm trying to get the post data in Java. Seems like it should be one of the simplest things to do right? I mean, HttpServletRequest.getParameter has to do it right? So how can you get the raw post data?
I found HttpServletRequest get JSON POST data and used Kdeveloper's code to pull the post data from a request. It works, but theres a catch: I can only get that post data once.
Heres the method I made from Kdeveloper's code:
public static String getPostData(HttpServletRequest req) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader reader = req.getReader();
reader.mark(10000);
String line;
do {
line = reader.readLine();
sb.append(line).append("\n");
} while (line != null);
reader.reset();
// do NOT close the reader here, or you won't be able to get the post data twice
} catch(IOException e) {
logger.warn("getPostData couldn't.. get the post data", e); // This has happened if the request's reader is closed
}
return sb.toString();
}
Previously I had closed the reader at the end of this method, but that caused exceptions when the method ran more than once on the same request. Without closing it, no exceptions happen, but the method returns an empty string.
Honestly, there should just be an exposed req.getPostData() method - did no one think that would be useful?
So how can I write this method such that it always returns the correct post data?

The request body is available as byte stream by HttpServletRequest#getInputStream():
InputStream body = request.getInputStream();
// ...
Or as character stream by HttpServletRequest#getReader():
Reader body = request.getReader();
// ...
Note that you can read it only once. The client ain't going to resend the same request multiple times. Calling getParameter() and so on will implicitly also read it. If you need to break down parameters later on, you've got to store the body somewhere and process yourself.

We had a situation where IE forced us to post as text/plain, so we had to manually parse the parameters using getReader. The servlet was being used for long polling, so when AsyncContext::dispatch was executed after a delay, it was literally reposting the request empty handed.
So I just stored the post in the request when it first appeared by using HttpServletRequest::setAttribute. The getReader method empties the buffer, where getParameter empties the buffer too but stores the parameters automagically.
String input = null;
// we have to store the string, which can only be read one time, because when the
// servlet awakens an AsyncContext, it reposts the request and returns here empty handed
if ((input = (String) request.getAttribute("com.xp.input")) == null) {
StringBuilder buffer = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while((line = reader.readLine()) != null){
buffer.append(line);
}
// reqBytes = buffer.toString().getBytes();
input = buffer.toString();
request.setAttribute("com.xp.input", input);
}
if (input == null) {
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.print("{\"act\":\"fail\",\"msg\":\"invalid\"}");
}

This worked for me: (notice that java 8 is required)
String requestData = request.getReader().lines().collect(Collectors.joining());
UserJsonParser u = gson.fromJson(requestData, UserJsonParser.class);
UserJsonParse is a class that shows gson how to parse the json formant.
class is like that:
public class UserJsonParser {
private String username;
private String name;
private String lastname;
private String mail;
private String pass1;
//then put setters and getters
}
the json string that is parsed is like that:
$jsonData: { "username": "testuser", "pass1": "clave1234" }
The rest of values (mail, lastname, name) are set to null

Related

nput stream read does not convert all data

I am writing application for Android devices, which have to communicate with some other device via bluetooth. (the other device its just a board like Raspery pi with attached bluetooth)
This other device accept list of command which I are single bytes like 'A', 'X', 'C' etc. and when you sending command it always returns some response which is something like 'OK', 'ERROR4' or somethoer data like '0000000000000001230120000000'.
I have implemented most of the command in my application and they work fine. But I have issue with last command which return 1748 bytes. Most of the time I am using this method to getting response, and it works fine:
private String send(byte [] bytes) {
if(bluetoothService == null)
return null;
if(bluetoothSocket == null)
return null;
String line = "";
Log.wtf("BYTESARR", Arrays.toString(bytes));
try {
OutputStream outputStream = bluetoothSocket.getOutputStream();
InputStream inputStream = bluetoothSocket.getInputStream();
outputStream.write(bytes);
outputStream.flush();
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
line = r.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return line;
}
But for that command where response is 1748 bytes method above freezing my app. So I implemented second method only for that command, which you see below.
OutputStream outputStream = bluetoothSocket.getOutputStream();
InputStream inputStream = bluetoothSocket.getInputStream();
outputStream.write(bytes);
outputStream.flush();
char [] b = new char[1540];
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
int from = 0;
for(int i = 0; i < EXCEPTIONS_NUMBER; i++){
int asdf = r.read(b, from, 50); // from and len are related to B not R
Log.wtf("READ", asdf + " <");
from += asdf;
}
line = new String(b);
Log.wtf("LINE", line);
But problem with that method is my response looks like this: (
...0000000000000������������...
Less then half of this response are 0 (zeros), which is what I was expecting, but second part of this response are those strange question marks(which I believe should be zeros) and I do not know why is that?
Question 1) Why first method making my app freeze when I am reading response with 1748bytes?
Question 2) Why second method is giving me that strange question marks instead of zeros?
EDIT [Solved]
I found the solution. So basically I have to use ByteArrayInputStream like this:
byte [] b = new byte[1538];
ByteArrayInputStream bais = new ByteArrayInputStream(b);
inputStream.read(b);
And that allows me read all bytes from b array and allows me to use bais if needed. I am not sure why this solution works, yet. But I am glad it worked. If someone understand it and can explain would be great. If not I will update post, when find out why it is working, if post will be not closed/deleted.
when you call r.readLine(), the method will not return until one of two conditions is met: the stream returns a line separator character, or the stream signals it has no more data. If neither of those things happen, your app will "freeze" indefinitely.
when you create an array of characters with new char[1540], it's filled with "NUL" characters - a non printing control character with ASCII/Unicode code 0 (don't confuse with "null" object reference in Java). While you read from the stream, you replace some of the content of the array with the characters you read, but some of the original NULs are left in place, and these make it to the string you create later.
To fix, create the string using the portion of the array that you've written to:
line = new String(b, 0, from);

why readLine() works, and readUTF() always throws IOException

I send a post request to an address, and this address will return a xml format data back.
I can print out the data byreadLine(), but when i use readUTF(), it always throws IOException. below is the code
DataInputStream input = new DataInputStream(urlCon.getInputStream());
String inputLine = "";
if((inputLine = input.readUTF()) != null) {
System.out.println(inputLine.toString());
}
input.close();
why readUTF() does not work? is it because the data is xml format?
If you're reading lines, use readLine(). Data to be read with readUTF() must have been written with writeUTF(). See the Javadoc.
NB readUTF() doesn't return null, so testing for it is futile.

Android Java Get simple .php page content into a String and save to internal storage

I'm developing an app. That app needs to get the content of a simple .php URL, and save it as a String.
The problem is that it is a very long String (VERY LONG) and it get's but in half. Take this link as an example:
http://thuum.org/download-dev-notes-web.php
With this code
URL notes = new URL("http://thuum.org/download-dev-notes-web.php")
BufferedReader in = new BufferedReader(new InputStreamReader(notes.openStream()));
String t = "";
while ((inputLine = in.readLine()) != null)
t = inputLine;
fOut = openFileOutput("notes", MODE_PRIVATE);
fOut.write(t.getBytes());
// Added This \/ to see it's length when divided, and it is not nearly as much as it should be
System.out.println(t.split("\\#").length);
Can someone tell me how would I be able to download that into a String, and save it into the internal storage without it getting cut? Some why it looks like it gets only the last x digits...
it seems you're overwriting your String t in every iteration of the while-loop. Try this:
StringBuilder result = new StringBuilder();
String inputLine = "";
while ((inputLine = in.readLine()) != null) {
result.append(inputLine);
}
fOut = openFileOutput("notes", MODE_PRIVATE);
fOut.write(result.toString().getBytes());
It creates a mutable StringBuilder and uses the resulting (immutable) String in the write call.
edit: I also recommend to always use curly brackets to indicate end of loop bodies, ommiting those can quickly lead to bugs, just check #gotofail for a recent example ;-)

Get web page content to String is very slow

I did the download a web page with the HttpURLConnection.getInputStream() and to get the content to a String, i do the following method:
String content="";
isr = new InputStreamReader(pageContent);
br = new BufferedReader(isr);
try {
do {
line = br.readLine();
content += line;
} while (line != null);
return content;
} catch (Exception e) {
System.out.println("Error: " + e);
return null;
}
The download of the page is fast, but the processing to get the content to String is very slow. There is another way faster to get the content to a String?
I transform it to String to insert in the database.
Read into buffer by number of bytes, not something arbitrary like lines. That alone should be a good start to speeding this up, as the reader will not have to find the line end.
Use a StringBuffer instead.
Edit for an example:
StringBuffer buffer=new StringBuffer();
for(int i=0;i<20;++i)
buffer.append(i.toString());
String result=buffer.toString();
use the blob/clob to put the content directly into database.
any specific reason for buliding string line by line and put it in the database??
I'm using jsoup to get specified content of a page and here is a web demo based on jquery and jsoup to catch any content of a web page, you should specify the ID or Class for the page content you need to catch: http://www.gbin1.com/technology/democenter/20120720jsoupjquerysnatchpage/index.html

How to retrieve raw post data from HttpServletRequest in java

I'm trying to get the post data in Java. Seems like it should be one of the simplest things to do right? I mean, HttpServletRequest.getParameter has to do it right? So how can you get the raw post data?
I found HttpServletRequest get JSON POST data and used Kdeveloper's code to pull the post data from a request. It works, but theres a catch: I can only get that post data once.
Heres the method I made from Kdeveloper's code:
public static String getPostData(HttpServletRequest req) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader reader = req.getReader();
reader.mark(10000);
String line;
do {
line = reader.readLine();
sb.append(line).append("\n");
} while (line != null);
reader.reset();
// do NOT close the reader here, or you won't be able to get the post data twice
} catch(IOException e) {
logger.warn("getPostData couldn't.. get the post data", e); // This has happened if the request's reader is closed
}
return sb.toString();
}
Previously I had closed the reader at the end of this method, but that caused exceptions when the method ran more than once on the same request. Without closing it, no exceptions happen, but the method returns an empty string.
Honestly, there should just be an exposed req.getPostData() method - did no one think that would be useful?
So how can I write this method such that it always returns the correct post data?
The request body is available as byte stream by HttpServletRequest#getInputStream():
InputStream body = request.getInputStream();
// ...
Or as character stream by HttpServletRequest#getReader():
Reader body = request.getReader();
// ...
Note that you can read it only once. The client ain't going to resend the same request multiple times. Calling getParameter() and so on will implicitly also read it. If you need to break down parameters later on, you've got to store the body somewhere and process yourself.
We had a situation where IE forced us to post as text/plain, so we had to manually parse the parameters using getReader. The servlet was being used for long polling, so when AsyncContext::dispatch was executed after a delay, it was literally reposting the request empty handed.
So I just stored the post in the request when it first appeared by using HttpServletRequest::setAttribute. The getReader method empties the buffer, where getParameter empties the buffer too but stores the parameters automagically.
String input = null;
// we have to store the string, which can only be read one time, because when the
// servlet awakens an AsyncContext, it reposts the request and returns here empty handed
if ((input = (String) request.getAttribute("com.xp.input")) == null) {
StringBuilder buffer = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while((line = reader.readLine()) != null){
buffer.append(line);
}
// reqBytes = buffer.toString().getBytes();
input = buffer.toString();
request.setAttribute("com.xp.input", input);
}
if (input == null) {
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.print("{\"act\":\"fail\",\"msg\":\"invalid\"}");
}
This worked for me: (notice that java 8 is required)
String requestData = request.getReader().lines().collect(Collectors.joining());
UserJsonParser u = gson.fromJson(requestData, UserJsonParser.class);
UserJsonParse is a class that shows gson how to parse the json formant.
class is like that:
public class UserJsonParser {
private String username;
private String name;
private String lastname;
private String mail;
private String pass1;
//then put setters and getters
}
the json string that is parsed is like that:
$jsonData: { "username": "testuser", "pass1": "clave1234" }
The rest of values (mail, lastname, name) are set to null

Categories