How can I handle 403s with XML.load in Scala?
I'm writing a Scala client to interface with Amazon's RESTful Product API. To return the XML for a given request I'm using:
XML.load(uri)
This is working fine, except that sometimes when Amazon doesn't like e.g. the Signature embedded in uri
and so开发者_StackOverflow it returns a 403 (along with a valid XML error report). Unfortunately this 403 throws an exception within XML.load
:
java.io.IOException: Server returned HTTP response code: 403 for URL: http://...
Ideally I'd like a way of flagging up the exception but returning the error XML anyway. Is there a simple way of doing this with XML.load
I'm missing, or will I need to break it down into a two-stage get-then-load process?
XML.load(URL) makes use of Java's java.net.URL.openStream(), which is throwing the IOException in a way that doesn't give you any access to the "Error Stream"
So, you can make a more long-winded sequence of calls to get an InputStream, and then you can XML.load this resulting InputStream.
Use URL.openConnection() (instead of URL.openStream), and then call HttpURLConnection.getInputStream() from within a try block. Then in your catch block, call HttpURLConnection.getErrorStream().
http://download.oracle.com/javase/6/docs/api/java/net/URL.html#openConnection%28%29
http://download.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html#getErrorStream%28%29
Now you can call XML.load(InputStream) on the resulting InputStream.
Hope that's enough to set you on your way.
In http dispatch (http://dispatch.databinder.net/About) this is doable like this:
import dispatch.Http._
import dispatch.{StatusCode, Http}
import xml.XML
try {
Http("http://localhost/notthere" <> println)
} catch {
case StatusCode(404, data) =>
println(XML.loadString(data))
}
This is the code I ended up writing, many thanks for all the guidance above. (If anything in the below could be improved/made more idiomatic, do please let me know):
val url = new URL("http://ecs.amazonaws...")
val connection = url.openConnection() match {
case x: HttpURLConnection => x
case _ => throw new ClassCastException
}
val responseCode = connection.getResponseCode();
val data = try {
connection.getInputStream()
} catch {
case e => connection.getErrorStream()
}
val xml = XML.load(data)
return (responseCode, xml)
For this level of control you may use ConstructingParser. An example is included in that link. You obtain the raw Source
and construct XML from it in two distinct steps using this technique.
Here is my solution which needs to add scalaj to project.
Step1: add scalaj to build.sbt (my scala version is 2.11.7 , but it still said my scala version was updated by one of libraray dependcies. so i add ivyscala to force update)
libraryDependencies += "org.scalaj" %% "scalaj-http" % "1.1.4"
ivyScala := ivyScala.value map { _.copy(overrideScalaVersion = true) }
Step2: import library to code
import scalaj.http.Http
import scala.xml.XML
Step3: use http body to get rid of 403; add these codes to exception part when you get 403 exception.
val response = Http(your_url)
val responseExecute = response.execute()
val responseBody = responseExecute.body.toString
val xml = XML.loadString(responseBody)
精彩评论