I'm trying to download all png files contained in an html file. I have trouble catching 404 status exceptions though, instead my program just crashes.
Here is some sample to demonstrate:
import Network.HTTP.Conduit
import qualified Data.ByteString.Lazy as L
main = do
let badUrl = "http://www.google.com/intl/en_com/images/srpr/WRONG.png"
imgData <- (simpleHttp badUrl) `catch` statusExceptionHandler
L.writeFile "my.png" imgData
statusExceptionHandler :: t -> IO L.ByteString
statusExceptionHandler e = (putStrLn "oops") >> (return L.empty)
My "oops" message never prints, instead app crashes with:
StatusCodeException (Status {statusCode = 404, statusMessage = "Not Found"}) [("Content-Type","text/html; charset=UTF-8"),("X-Content-Type-Options","nosniff"),("Date","Fri, 27 Jan 2012 03:10:34 GMT"),("Server","sffe"),("Content-Length","964"),("X-XSS-Protection","1; mode=block")]
What am I doing wrong?
Update:
Following Thoma's advice, I changed my code to the following snippet and now have proper exception handling in place.
main = do
let badUrl = "http://www.google.com/intl/en_com/images/srpr/WRONG.png"
imgData <- (simpleHttp badUrl) `X.catch` statusExceptionHandler
case imgData of x | x == L.empty -> return ()
| otherwise -> L.writeFile "my.png" imgData
statusExceptionHandler :: HttpException -> IO L.ByteString
statusExceptionHandler (StatusCodeException status headers) =
putStr "An error occured during download: "
>> (putStrLn $ show status)
>> (return L.empty)
You should probably read the Marlow paper on extensible exceptions. The original catch
, exported by Prelude and used in your code snipt, only works for IOError's. The http-conduit code is throwing exceptions of a different type, HttpException to be exact. (there is some dynamic typing going on via the Typeable class, see the paper).
The solution? Use catch from Control.Exception and only catch the error types you want to handle (or SomeException
for all of them).
import Network.HTTP.Conduit
import qualified Data.ByteString.Lazy as L
import Control.Exception as X
main = do
let badUrl = "http://www.google.com/intl/en_com/images/srpr/WRONG.png"
imgData <- (simpleHttp badUrl) `X.catch` statusExceptionHandler
L.writeFile "my.png" imgData
statusExceptionHandler :: SomeException -> IO L.ByteString
statusExceptionHandler e = (putStrLn "oops") >> (return L.empty)
In addition to Thomas's answer, you could tell http-conduit
not to throw an exception by overriding the checkStatus
record of your Request
type.
来源:https://stackoverflow.com/questions/9028636/how-can-i-catch-a-404-status-exception-thrown-by-simplehttp-of-http-conduit