I am trying to read the response body for a webservice request which is a POST method.
I have tried changing the method type to GET just to try even though it is a POST request it did not work. However, it does give me a response code '200' yet while trying to read the response body with a reader i get error '405'.
Could anyone please give me some guidance on this.
I need to read the entire response body and then save one item which is a dynamic alphanumeric string to another property file.
Thanks in advance.
`
try{
URL url = new URL(myUrl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod(POST);
conn.connect();
int respCode = conn.getResponseCode();
if(respCode != 200)
{
System.out.println("connection refused");
}
else{
Scanner sc = new Scanner(url.openStream());
while(sc.hasNext()){
inline = sc.nextLine();
System.out.println(inline);
sc.close();
}
}catch(Exception e){
e.prinstackTrace();
}
`
Response Codes which are in the form of 4xx are client error codes. Normally, 405 would mean "Method Not Allowed" but I have seen them sometimes when the client is unable to understand the type of response as well.
You should add headers - Content-Type and Accept in your request and then see the output.
It might solve your problem.
Related
I am trying to perform a get request using Groovy using the below code:
String url = "url of endpoint"
def responseXml = new XmlSlurper().parse(url)
If the endpoint returns status as 200 then everything works good but there is one case where we have to validate the error response like below and status returned is 400:
<errors>
<error>One of the following parameters is required: xyz, abc.</error>
<error>One of the following parameters is required: xyz, mno.</error>
</errors>
In this case parse method throws :
java.io.IOException: Server returned HTTP response code: 400 for URL: "actual endpoint throwing error"
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1900)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:646)
at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:150)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:831)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:796)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:142)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1216)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:644)
at groovy.util.XmlSlurper.parse(XmlSlurper.java:205)
at groovy.util.XmlSlurper.parse(XmlSlurper.java:271)
Can anyone pls suggest how to handle if server give error message by throwing 400 status code?
In the question since we are getting 400 status code for GET request. So in built XmlSlurper().parse(URI) method does not work as it throw io.Exception.
Groovy also support HTTP methods for api request and response and the below worked for me:
def getReponseBody(endpoint) {
URL url = new URL(endpoint)
HttpURLConnection get = (HttpURLConnection)url.openConnection()
get.setRequestMethod("GET")
def getRC = get.getResponseCode()
BufferedReader br = new BufferedReader(new InputStreamReader(get.getErrorStream()))
StringBuffer xmlObject = new StringBuffer()
def eachLine
while((eachLine = br.readLine()) !=null){
xmlObject.append(eachLine)
}
get.disconnect()
return new XmlSlurper().parseText(xmlObject.toString())
}
Getting the response text from the HttpURLConnection class rather than implicitly through XmlSlurper allows you much more flexibility in handling unsuccessful responses. Try something like this:
def connection = new URL('https://your.url/goes.here').openConnection()
def content = { ->
try {
connection.content as String
} catch (e) {
connection.responseMessage
}
}()
if (content) {
def responseXml = new XmlSlurper().parseText(content)
doStuffWithResponseXml(responseXml)
}
Even better would be to use an actual full-featured HTTP client, like the Spring Framework's HttpClient or RestTemplate classes.
You should check the return code and than obtain the error stream from http request instance in case of an error. The problem itself has nothing to do with JsonSlurper, as no instance of "input stream" is returned from http request instance if service returns not successfull return codes (400, 401, 500 etc.) POST example can be seen below:
http= new URL("yourUrl").openConnection() as HttpURLConnection
http.setRequestMethod('POST')
http.setDoOutput(true)
http.setRequestProperty("Content-Type", 'application/json')
http.setRequestProperty("Accept", 'application/json')
http.setRequestProperty("Authorization", "Bearer $yourTokenVariable")
http.outputStream.write(data.getBytes("UTF-8"))
http.connect()
if(http.getResponseCode() != 200 && http.getResponseCode() != 201){
throw new InvalidInputException("There was an error: " + http.getErrorStream().getText("UTF-8"))
} else {
//You can take input stream here
}
I am using Parse REST API for a game, altough there are java libraries for Parse I would like to handle the transfer protocolls my self using java.net for learning purposes. Please look away from things like, why dont I use Apache HttpClient.
Following the Parse REST API Guide
Here is what I am trying to achive:
Signup
User Login
validating session tokens / Retriving current user
The first two steps works just fine, the former using POST request method and the latter using GET with some paramaters.
Keeping the Request and Response format in mind I also provide the Application-ID and the REST-API-Key which are the appropriate request headers needed.
Now, for the third step using GET request with no paramaters, but with an additional header, the API expects there to be a Session-Token provided.
Code
private static void validateSessionToken(String token) throws IOException {
System.out.println("Token: " + token);
URL url = new URL("https://api.parse.com/1/users/me");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("X-Parse-Application-Id", "xxxxxxx");
con.setRequestProperty("X-Parse-REST-API-Key", "xxxxxxxx");
con.setRequestProperty("X-Parse-Session-Token", token);
con.setRequestProperty("content-type", "application/json");
int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + url);
System.out.println("Response Code : " + responseCode);
if(responseCode == 400) {
System.out.println("Bad request!");
return;
}
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
}
Outputs
Sending 'GET' request to URL : https://api.parse.com/1/users/me
Response Code : 400
Bad request!
Debugging
I have been using the PARSE API CONSOLE and Chrome network debugging tool to try and see what the difference is, but cannot see any.
From wiki:
400 Bad Request
The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)
Some photos that may help
So when I asked my JsonObject for the session token:
jsonObject.get("sessionToken").toString();
It returned: "r:WzB7qdmhkcW5qd2moM8gbBLDp" in quotation, but using:
jsonObject.get("sessionToken").getAsString();
it returned: r:WzB7qdmhkcW5qd2moM8gbBLDp
I was so focused on the request I did not even notice...
I have a bit of Java code that attempts to send POST data over to t Django application. However, the view is simply never called. If I paste the same URL the java code hits into my browser, the Django view is called. I have no idea what I am missing, but something must be wrong with the Java write.
This is the Java function doing the write:
public void executeWrite(String requestUrl, JsonObject jsonObject)
{
DataInputStream input = null;
try
{
URL url;
HttpURLConnection urlConn;
DataOutputStream printout;
System.out.println(requestUrl);
// URL of CGI-Bin script.
url = new URL (requestUrl);
// URL connection channel.
urlConn = (HttpURLConnection)url.openConnection();
// Let the run-time system (RTS) know that we want input.
urlConn.setDoInput (true);
// Let the RTS know that we want to do output.
urlConn.setDoOutput (true);
// No caching, we want the real thing.
urlConn.setUseCaches (false);
// Specify the content type.
urlConn.setRequestMethod("POST");
urlConn.setRequestProperty("content-type","application/json; charset=utf-8");
OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
wr.write(jsonObject.toString());
wr.flush();
wr.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
Now the requestURL passed into the function directly corresponds to the one for the Django view. The requestURL is:
http://127.0.0.1:8000/events/rest/33456/create
This is the Django Urlconfig:
(r'^events/rest/(?P<key>\d+)/create', 'events.views.restCreateEvent'),
Finally this is the view that never gets called by the Java code
#csrf_exempt
def restCreateEvent(request, key):
#doesn't really matter what is in here it never runs
So, what am I doing wrong that the POST request is never received by the Django sever? I've spent about 2 hours trying to figure it out and I can't find any issues with the Java code. Clearly something is wrong though.
Make sure your view is csrf exempt since you are not sending the appropriate CSRF token from the Java request.
I think the crsf thing was actually the issue. Once I added that I changed the Java code slightly and it worked. I am still not sure what the subtle Java error was, here is the working Java code.
public void executeWrite(String requestUrl, JsonObject jsonObject)
{
InputStreamReader input = null;
try
{
URL url;
HttpURLConnection urlConn;
DataOutputStream printout;
System.out.println(requestUrl);
// URL of CGI-Bin script.
url = new URL (requestUrl);
// URL connection channel.
urlConn = (HttpURLConnection)url.openConnection();
// Let the run-time system (RTS) know that we want input.
urlConn.setDoInput (true);
// Let the RTS know that we want to do output.
urlConn.setDoOutput (true);
// No caching, we want the real thing.
urlConn.setUseCaches (false);
// Specify the content type.
urlConn.setRequestMethod("POST");
urlConn.setRequestProperty("content-type","application/json; charset=utf-8");
OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
wr.write(jsonObject.toString());
wr.flush();
wr.close();
input = new InputStreamReader (urlConn.getInputStream ());
String response = UserInterface.read(new BufferedReader(input));
if(response.length() > 0)
{
System.out.println("Response:" + response);
}
input.close();
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
I remember the URL need change to "http://127.0.0.1:8000/events/rest/33456/create/" when use "POST" type.
I am trying to send a post request to a url using HttpURLConnection (for using cUrl in java).
The content of the request is xml and at the end point, the application processes the xml and stores a record to the database and then sends back a response in form of xml string. The app is hosted on apache-tomcat locally.
When I execute this code from the terminal, a row gets added to the db as expected. But an exception is thrown as follows while getting the InputStream from the connection
java.io.FileNotFoundException: http://localhost:8080/myapp/service/generate
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1401)
at org.kodeplay.helloworld.HttpCurl.main(HttpCurl.java:30)
Here is the code
public class HttpCurl {
public static void main(String [] args) {
HttpURLConnection con;
try {
con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setDoInput(true);
File xmlFile = new File("test.xml");
String xml = ReadWriteTextFile.getContents(xmlFile);
con.getOutputStream().write(xml.getBytes("UTF-8"));
InputStream response = con.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(response));
for (String line ; (line = reader.readLine()) != null;) {
System.out.println(line);
}
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Its confusing because the exception is traced to the line InputStream response = con.getInputStream(); and there doesn't seem to be any file involved for a FileNotFoundException.
When I try to open a connection to an xml file directly, it doesn't throw this exception.
The service app uses spring framework and Jaxb2Marshaller to create the response xml.
The class ReadWriteTextFile is taken from here
Thanks.
Edit:
Well it saves the data in the DB and sends back a 404 response status code at the same time.
I also tried doing a curl using php and print out the CURLINFO_HTTP_CODE which turns out to be 200.
Any ideas on how do I go about debugging this ? Both service and client are on the local server.
Resolved:
I could solve the problem after referring to an answer on SO itself.
It seems HttpURLConnection always returns 404 response when connecting to a url with a non standard port.
Adding these lines solved it
con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
I don't know about your Spring/JAXB combination, but the average REST webservice won't return a response body on POST/PUT, just a response status. You'd like to determine it instead of the body.
Replace
InputStream response = con.getInputStream();
by
int status = con.getResponseCode();
All available status codes and their meaning are available in the HTTP spec, as linked before. The webservice itself should also come along with some documentation which overviews all status codes supported by the webservice and their special meaning, if any.
If the status starts with 4nn or 5nn, you'd like to use getErrorStream() instead to read the response body which may contain the error details.
InputStream error = con.getErrorStream();
FileNotFound is just an unfortunate exception used to indicate that the web server returned a 404.
To anyone with this problem in the future, the reason is because the status code was a 404 (or in my case was a 500). It appears the InpuStream function will throw an error when the status code is not 200.
In my case I control my own server and was returning a 500 status code to indicate an error occurred. Despite me also sending a body with a string message detailing the error, the inputstream threw an error regardless of the body being completely readable.
If you control your server I suppose this can be handled by sending yourself a 200 status code and then handling whatever the string error response was.
For anybody else stumbling over this, the same happened to me while trying to send a SOAP request header to a SOAP service. The issue was a wrong order in the code, I requested the input stream first before sending the XML body. In the code snipped below, the line InputStream in = conn.getInputStream(); came immediately after ByteArrayOutputStream out = new ByteArrayOutputStream(); which is the incorrect order of things.
ByteArrayOutputStream out = new ByteArrayOutputStream();
// send SOAP request as part of HTTP body
byte[] data = request.getHttpBody().getBytes("UTF-8");
conn.getOutputStream().write(data);
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.d(TAG, "http response code is " + conn.getResponseCode());
return null;
}
InputStream in = conn.getInputStream();
FileNotFound in this case was an unfortunate way to encode HTTP response code 400.
FileNotFound in this case means you got a 404 from your server - could it be that the server does not like "POST" requests?
FileNotFound in this case means you got a 404 from your server
You Have to Set the Request Content-Type Header Parameter
Set “content-type” request header to “application/json” to send the request content in JSON form.
This parameter has to be set to send the request body in JSON format.
Failing to do so, the server returns HTTP status code “400-bad request”.
con.setRequestProperty("Content-Type", "application/json; utf-8");
Full Script ->
public class SendDeviceDetails extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
String data = "";
String url = "";
HttpURLConnection con = null;
try {
// From the above URL object,
// we can invoke the openConnection method to get the HttpURLConnection object.
// We can't instantiate HttpURLConnection directly, as it's an abstract class:
con = (HttpURLConnection)new URL(url).openConnection();
//To send a POST request, we'll have to set the request method property to POST:
con.setRequestMethod("POST");
// Set the Request Content-Type Header Parameter
// Set “content-type” request header to “application/json” to send the request content in JSON form.
// This parameter has to be set to send the request body in JSON format.
//Failing to do so, the server returns HTTP status code “400-bad request”.
con.setRequestProperty("Content-Type", "application/json; utf-8");
//Set Response Format Type
//Set the “Accept” request header to “application/json” to read the response in the desired format:
con.setRequestProperty("Accept", "application/json");
//To send request content, let's enable the URLConnection object's doOutput property to true.
//Otherwise, we'll not be able to write content to the connection output stream:
con.setDoOutput(true);
//JSON String need to be constructed for the specific resource.
//We may construct complex JSON using any third-party JSON libraries such as jackson or org.json
String jsonInputString = params[0];
try(OutputStream os = con.getOutputStream()){
byte[] input = jsonInputString.getBytes("utf-8");
os.write(input, 0, input.length);
}
int code = con.getResponseCode();
System.out.println(code);
//Get the input stream to read the response content.
// Remember to use try-with-resources to close the response stream automatically.
try(BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"))){
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(response.toString());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
}
}
return data;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.e("TAG", result); // this is expecting a response code to be sent from your server upon receiving the POST data
}
and call it
new SendDeviceDetails().execute("");
you can find more details in this tutorial
https://www.baeldung.com/httpurlconnection-post
The solution:
just change localhost for the IP of your PC
if you want to know this: Windows+r > cmd > ipconfig
example: http://192.168.0.107/directory/service/program.php?action=sendSomething
just replace 192.168.0.107 for your own IP (don't try 127.0.0.1 because it's same as localhost)
Please change
con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();
To
con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();
I am trying to connect to a URL from a desktop app, and I get the error indicated in the Title of my question, but when I tried to connect to the same URL from servlet, all works fine. When I load the URL from browser, all works fine. I am using the same code in the servlet. The code was in a library, when it didn't work, I pulled the code out to a class in the current project, yet it didn't work.
The URL https://graph.facebook.com/me.
The Code fragment.
public static String post(String urlSpec, String data) throws Exception {
URL url = new URL(urlSpec);
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(data);
writer.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = "";
StringBuilder builder = new StringBuilder();
while((line = reader.readLine()) != null) {
builder.append(line);
}
return builder.toString();
}
I'm a little bit confused here, is there something that is present is a servlet that is not a normal desktop app?
Thanks.
FULL STACK TRACE
Feb 8, 2011 9:54:14 AM com.trinisoftinc.jiraffe.objects.FacebookAlbum create
SEVERE: null
java.io.IOException: Server returned HTTP response code: 400 for URL: https://graph.facebook.com/me
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1313)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
at com.jiraffe.helpers.Util.post(Util.java:49)
at com.trinisoftinc.jiraffe.objects.FacebookAlbum.create(FacebookAlbum.java:211)
at com.trinisoftinc.jiraffe.objects.FacebookAlbum.main(FacebookAlbum.java:261)
EDIT: You need to find the exact error message that facebook is sending in the response
You can modify your code to get the message from the error stream like so:
HttpURLConnection httpConn = (HttpURLConnection)connection;
InputStream is;
if (httpConn.getResponseCode() >= 400) {
is = httpConn.getErrorStream();
} else {
is = httpConn.getInputStream();
}
Take a look at how you are passing the user context
Here's some information that could help you out:
Look at the error message behind the 400 response code:
"Facebook Platform" "invalid_request" "An active access token must be used to query information about the current user*
You'll find the solution here
HTTP/1.1 400 Bad Request
...
WWW-Authenticate: OAuth "Facebook Platform" "invalid_request" "An active access token must be used to query information about the current user."
...
I finally found the problem. Of course it's my code. One part of the code I didn't post is the value of data. data must contain only name and description but I am passing more than name and description.