My Galaxy Nexus arrived today, and one of the first things I did was to load my app onto it so I could demonstrate it to my friends. Part of its functionality involves importing RSS Feeds from Google Reader. However, upon trying this, I was getting 405 Method Not Allowed errors.
This problem is Ice Cream Sandwich-specific. The code I've attached works fine on Gingerbread and Honeycomb. I've traced the error down to the moment the connection is made, when the GET request magically turns into a POST request.
/**
* Get the authentication token from Google
* #param auth The Auth Key generated in getAuth()
* #return The authentication token
*/
private String getToken(String auth) {
final String tokenAddress = "https://www.google.com/reader/api/0/token";
String response = "";
URL tokenUrl;
try {
tokenUrl = new URL(tokenAddress);
HttpURLConnection connection = (HttpURLConnection) tokenUrl.openConnection();
connection.setRequestMethod("GET");
connection.addRequestProperty("Authorization", "GoogleLogin auth=" + auth);
connection.setRequestProperty("Content-Type","application/x-www-form-urlendcoded");
connection.setUseCaches(false);
connection.setDoOutput(true);
Log.d(TAG, "Initial method: " + connection.getRequestMethod()); // Still GET at this point
try {
connection.connect();
Log.d(TAG, "Connected. Method is: " + connection.getRequestMethod()); // Has now turned into POST, causing the 405 error
InputStream in = new BufferedInputStream(connection.getInputStream());
response = convertStreamToString(in);
connection.disconnect();
return response;
}
catch (Exception e) {
Log.d(TAG, "Something bad happened, response code was " + connection.getResponseCode()); // Error 405
Log.d(TAG, "Method was " + connection.getRequestMethod()); // POST again
Log.d(TAG, "Auth string was " + auth);
e.printStackTrace();
connection.disconnect();
return null;
}
}
catch(Exception e) {
// Stuff
Log.d(TAG, "Something bad happened.");
e.printStackTrace();
return null;
}
}
Is there anything that could be causing this problem? Could this function be better coded to avoid this problem?
Many thanks in advance.
This behaviour is described in Android Developers: HttpURLConnection
HttpURLConnection uses the GET method by default. It will use POST if
setDoOutput(true) has been called.
What's strange though is that this has not actually been the behaviour until 4.0, so I would imagine it's going to break many existing published apps.
There is more on this at Android 4.0 turns GET into POST.
Removing this line worked for me:
connection.setDoOutput(true);
4.0 thinks with this line it should definitely be POST.
Get rid of this:
connection.setRequestProperty("Content-Type","application/x-www-form-urlendcoded");
This tells the API this is a POST.
UPDATE on how it could be done via HttpClient:
String response = null;
HttpClient httpclient = null;
try {
HttpGet httpget = new HttpGet(yourUrl);
httpget.setHeader("Authorization", "GoogleLogin auth=" + auth);
httpclient = new DefaultHttpClient();
HttpResponse httpResponse = httpclient.execute(httpget);
final int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new Exception("Got HTTP " + statusCode
+ " (" + httpResponse.getStatusLine().getReasonPhrase() + ')');
}
response = EntityUtils.toString(httpResponse.getEntity(), HTTP.UTF_8);
} catch (Exception e) {
e.printStackTrace();
// do some error processing here
} finally {
if (httpclient != null) {
httpclient.getConnectionManager().shutdown();
}
}
This is one that got me - basically by setting setDoOutput(true) it forces a POST request when you make the connection, even if you specify this is a GET in the setRequestMethod:
HttpURLConnection uses the GET method by default. It will use POST if
setDoOutput(true) has been called. Other HTTP methods (OPTIONS, HEAD,
PUT, DELETE and TRACE) can be used with setRequestMethod(String).
This caught me a while back - very frustrating ...
See http://developer.android.com/reference/java/net/HttpURLConnection.html and go to HTTP Methods heading
I've found that pre-ICS one could get away with making a body-less POST without providing a Content-Length value, however post-ICS you must set Content-Length: 0.
Related
I am working on a personal project to learn how to work with Rest web services.
I have an API web application in Visual, which is my controller and where the connection to Oracle is made, and a web application in JAVA and with a JSON library, in addition to trying to do it in layers according to what I learned in the institute.
When I make the GET request, I have no problem, they bring me the data, but when I make a POST request as a customer's registrar it is when the problems start and I get the error 411 in java.
I Read looking for the solution that some worked for them by placing the "Content-Length" I don't know if I put it right but I still have the problem.
public int insertarCliente(Cliente c){
globalURL += "?rut=" + c.getRut() + "&nom="+ c.getNombre() +"&app=" + c.getApellidoP() + "&apm=" + c.getApellidoM();
try {
HttpURLConnection conn = Conectar(globalURL);
conn.setRequestMethod("POST");
conn.setRequestProperty("ACCEPT", "application/json");
conn.setRequestProperty("Content-Length", "0");
if (conn.getResponseCode() == 200) {
//InputStreamReader in = new InputStreamReader(conn.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String resp = br.readLine();
JSONObject obj = new JSONObject(resp);
return obj.getInt("resp");
}
} catch (Exception e) {
Logger.getLogger(ClienteDAO.class.getName()).log(Level.SEVERE, null, e);
}
return 0;
}
The problem start in the IF.
And the error that shows me is the following:
Glook2 was successfully deployed in 227 milliseconds.
**Grave: java.lang.RuntimeException: Failed : HTTP Error code : 411**
at Controllers.ClienteDAO.insertarCliente(ClienteDAO.java:50)
at Services.cliente.registrar(cliente.java:104)
at Services.cliente.processRequest(cliente.java:46)
at Services.cliente.doPost(cliente.java:77)
I must emphasize that I have proven in the POSTMAN that the web services method works and correctly adds the data to the database.
String globalURL = "http://localhost:60367/api/Cliente";
HttpURLConnection conn;
public ClienteDAO() {
conn = Conectar(this.globalURL);
}
private HttpURLConnection Conectar(String urlRest) {
try {
URL url;
url = new URL(urlRest);
return (HttpURLConnection) url.openConnection();
} catch (Exception e) {
Logger.getLogger(ClienteDAO.class.getName()).log(Level.SEVERE, null, e);
}
return null;
}
Enable logging as shown here: How to enable wire logging for a java HttpURLConnection traffic?
You will then see1 that the Content-Length header is not sent:
FINE: sun.net.www.MessageHeader#4bf558aa5 pairs:
{POST / HTTP/1.1: null}
{ACCEPT: application/json}
{User-Agent: Java/13}
{Host: localhost:8080}
{Connection: keep-alive}
1: Sample log entry, wrapped for easier reading
That is because HttpURLConnection manages that header.
To send a Content-Length: 0 header, send no output, i.e. replace:
conn.setRequestProperty("Content-length", "0");
with:
conn.setDoOutput(true);
conn.getOutputStream().close();
Logging now shows the header:
FINE: sun.net.www.MessageHeader#5fa7e7ff7 pairs:
{POST / HTTP/1.1: null}
{ACCEPT: application/json}
{User-Agent: Java/13}
{Host: localhost:8080}
{Connection: keep-alive}
{Content-type: application/x-www-form-urlencoded}
{Content-Length: 0}
See also: JDK-6997628: HttpURLConnection strips Content-Length header on Post:
Affects Version/s: 6u22
Status: Open
Resolution: Unresolved
BT2:EVALUATION
The fix for CR 6961084 restricts the setting of some potentially security sensitive headers. Since these headers were allowed to be set in previous releases then of course compatibility is effected. A decision was made that compatibility was secondary to the security risk these headers posed. We understand that there may be valid apps out there that will be effected by this, so the sun.net.http.allowRestrictedHeaders property was added to revert to previous behavior.
BT2:WORK AROUND
Run with -Dsun.net.http.allowRestrictedHeaders=true
I would not recommend using that workaround.
I need to make a webcrawler to gather links and information from a specific website. I also need to use Apache HTTP Client to do, and I've been glancing and looking over the tutorial on the website for a few days and I'm getting nowhere. Right now, I'm trying to figure out how to use apache HTTPClient to grab the HTML so I can parse through it. Frankly, it may be a case of misunderstanding what HTTPClient is supposed to be used for. Anything help would be greatly appreciated.
h-m-m... here it is, but... do not be surprised if you will see not what you see in the browser. As I said you will get what server actually returns by request:
HttpClient client = new HttpClient();
HostConfiguration hostConfig = new HostConfiguration();
hostConfig.setHost("my.site.com", 80, Protocol.getProtocol("http"));
client.setHostConfiguration(hostConfig);
GetMethod getHtmlPageMethod = new GetMethod("/myPage.html");
getHtmlPageMethod.setFollowRedirects(true);
try {
int responseCode = client.executeMethod(getHtmlPageMethod);
System.out.println("Got response code: " + responseCode);
if (200 == responseCode) {
System.out.println("Response code 200 - SUCCESS ... go for response body... ");
String responseBody = getHtmlPageMethod.getResponseBodyAsString();
if (null != responseBody) {
System.out.println("Got body string:" + System.lineSeparator());
System.out.println(responseBody);
} else
{
System.out.println("No response body returned!");
}
}
} catch (Exception e) {
e.printStackTrace();
}
I'm trying to proceed the JIRA transitions by java coding, most of the time it works, but the jira rest api call sometimes return the below error: (Actually with this error, the transition is processed)
java.io.IOException: Server returned HTTP response code: 400 for URL: http://testingSite/jira/rest/api/latest/issue/ABC-123/transitions
Also, there are some case the rest api call do not return errors but the transition is not proceed.
Here is my coding, most of the time it works, so it ruined my days to figure out what is happening.
try {
String authkey = "YWRtaW46cGFzc3dvcmQ=";
URL url = new URL("http://testingSite/jira/rest/api/latest/issue/ABC-123/transitions");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Authorization", "Basic " + authkey);
connection.setRequestProperty("Accept-Charset", "UTF-8");
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
OutputStream os = connection.getOutputStream();
String data = "{\"transition\": {\"id\": \"71\"}}";
os.write(data.toString().getBytes("UTF-8"));
os.close();
content = connection.getInputStream();
in = new InputStreamReader(content);
br = new BufferedReader(in);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
The error comes from this line:
content = connection.getInputStream();
I expect there is not exception and all transition is processed but the behaviour is quite strange for me.
Behaviour 1 : Server returned HTTP response code : 400 but the transition is processed
Behaviour 2 : Server do not return any error but the transition is not processed
So I was looking for reference documentation here. And its stated that
POST: 400 - If there is no transition specified.
You have you transition id hardcoded and maybe for that type of issue it has different transition id or something similar. Try to call
GET /rest/api/2/issue/{issueIdOrKey}/transitions?{transitionId}
to verify you transition is actually there.
I am trying to get the html source of a webpage through java code using Jsoup. Below is the code I am using to fetch the page. I am getting a 500 Internal Server Error.
String encodedUrl = URIUtil.encodePathQuery(urlToFetch.trim(), "ISO-8859-1");
Response res = Jsoup.connect(encodedUrl)
.header("Accept-Language", "en")
.userAgent(userAgent)
.data(data)
.maxBodySize(bodySize)
.ignoreHttpErrors(true)
.ignoreContentType(true)
.timeout(10000)
.execute();
However, when I fetch the same page with wget from command line, it works. A simple HttpClient from code also works.
// Create an instance of HttpClient.
HttpClient client = new HttpClient();
// Create a method instance.
GetMethod method = new GetMethod(url);
// Provide custom retry handler is necessary
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
try {
// Execute the method.
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
// Read the response body.
byte[] responseBody = method.getResponseBody();
// Deal with the response.
// Use caution: ensure correct character encoding and is not binary data
System.out.println(new String(responseBody));
} catch (HttpException e) {
System.err.println("Fatal protocol violation: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("Fatal transport error: " + e.getMessage());
e.printStackTrace();
} finally {
// Release the connection.
method.releaseConnection();
}
Is there anything I would need to change in the parameters for Jsoup.connect() method for it work?
This however does not happen for all urls. It is specifically happening for pages from this website:
http://xyo.net/iphone-app/instagram-RrkBUFE/
You need Accept header.
Try this:
String encodedUrl = "http://xyo.net/iphone-app/instagram-RrkBUFE/";
Response res = Jsoup.connect(encodedUrl)
.header("Accept-Language", "en")
.ignoreHttpErrors(true)
.ignoreContentType(true)
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.followRedirects(true)
.timeout(10000)
.method(Connection.Method.GET)
.execute();
System.out.println(res.parse());
It works.
Please also note that the site is trying to set cookies, you may need to handle them.
Hope it will help.
I have a problem with a WebService on Android. I am getting a 400 error but there is no information on the ErrorStream.
What I am trying to do is a POST request on a WCF Webservice using JSON.
I must add that I have includeExceptionDetailInFaults Enabled on my Service. The last time I got a 400 error, it was because I hadn't defined the RequestProperty. Now I don't get any error in the stream.
Here is the code:
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
// In my last error I had not included these lines. Maybe they are still wrong?
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setChunkedStreamingMode(0);
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(out);
outputStreamWriter.write(jsonObject.toString(), 0, jsonObject.length());
outputStreamWriter.flush();
//outputStreamWriter.close();
int code = urlConnection.getResponseCode();
System.out.println(code);
if(code == 400) {
BufferedInputStream errorStream = new BufferedInputStream(urlConnection.getErrorStream());
InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
BufferedReader bufferedReader = new BufferedReader(errorStreamReader);
StringBuilder builder = new StringBuilder();
String aux = "";
while ((aux = bufferedReader.readLine()) != null) {
builder.append(aux);
}
String output = builder.toString(); // The output is empty.
System.out.print(output);
}
Check Retrofit library from Square it's more easy and thin for GET/POST request and especially for REST. I suggest you to try it. It will make your life easy.
You can use different JSON parsers, error handlers, etc. Very flexible.
POST request definition using retrofit it's simple like this:
An object can be specified for use as an HTTP request body with the #Body annotation.
#POST("/users/new")
void createUser(#Body User user, Callback<User> cb);
Methods can also be declared to send form-encoded and multipart data.
Form-encoded data is sent when #FormUrlEncoded is present on the method. Each key-value pair is annotated with #Field containing the name and the object providing the value.
#FormUrlEncoded
#POST("/user/edit")
User updateUser(#Field("first_name") String first, #Field("last_name") String last);
After you define method inside your Java interface like shown above instantiate it:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.soundcloud.com")
.build();
MyInterface service = restAdapter.create(MyInterface.class);
And then you can call your method synchronously or asynchronously (in case you pass Callback instance).
service.myapi(requestBody);
See Retrofit documentation (http://square.github.io/retrofit/javadoc/index.html) and samples on GitHub for more details.
A 400 error might be occuring (and usually occurs in my case) because of incorrect URL or bad JSON format in post. please check those two
Using an HttpPost object will make your job a lot easier in my opinion
HttpPost post = new HttpPost(url);
if(payload != null){
try {
StringEntity entity = new StringEntity(payload,HTTP.UTF_8);
entity.setContentType(contentType);
post.setEntity(entity);
} catch (UnsupportedEncodingException e) {
LOG.d(TAG, "post err url : " + url);
LOG.e(TAG, "post err url" , e);
throw new Exception(1, e);
}
}
HttpResponse response=executeRequest(owner, post);