在 Haskell 中使用 Network.URL 库

Using Network.URL library in Haskell

读完 Yesod 书后,我对使用类型安全 URL 而不是原始字符串之类的想法深信不疑。我只是在尝试一些简单的获取请求,而不是使用完整的 yesod 库,但似乎很容易定义自定义 URL 数据类型派生显示,或者使用其他一些现有的最小 URL图书馆。

我在 Hoogle 上找到了这个: https://hackage.haskell.org/package/url-2.1.3/docs/Network-URL.html

似乎它会做我想要的那种验证,所以像这样的错误:

myothervar = "kittens"   -- oh no, I defined this for something else
parseRequest myothervar  -- and called it from parseRequest!

会被编译器捕获,而

myurl = "http://example.com"
parseRequest myurl

...一定会成功的,太好了

ghci    λ> import Network.URL
ghci    λ> myurl = importURL "http://example.com"

文档提示我需要使用 importURL

ghci    λ> :t myurl
myurl :: Maybe URL

太好了,我知道怎么去-Maybe,我用Just

ghci    λ> exportURL $ Just myurl

<interactive>:47:13: error:
    • Couldn't match expected type ‘URL’
                  with actual type ‘Maybe (Maybe URL)’
    • In the second argument of ‘($)’, namely ‘Just myurl’
      In the expression: exportURL $ Just myurl
      In an equation for ‘it’: it = exportURL $ Just myurl

ghci    λ> Just (Just (URL {url_type = Absolute (Host {protocol = HTTP False, host = "example.com", port = Nothing}), url_path = "", url_params = []}))

好的,什么?那并没有删除我的 Just,它只是添加了另一个 Just。如何检索 parseRequest 将作为参数的字符串?

看来我确实需要通过某种方式导出回 String 来消除它:

ghci    λ> import Network.HTTP.Conduit
ghci    λ> parseRequest $ myurl

<interactive>:84:16: error:
    • Couldn't match type ‘Maybe URL’ with ‘[Char]’
      Expected type: String
        Actual type: Maybe URL

这里有几件事值得讨论:

OK, what? That didn't remove my Just, it just added another Just. How can I retrieve the String that parseRequest will take as an argument?

记住类型为 Maybe URL 的东西是 NothingJust url,其中 url :: URL。你不能只 "extract" URL 的某些部分,因为 Maybe 的重点是 你可能根本没有 URL!

How can I retrieve the String that parseRequest will take as an argument?

这是不必要的 - parseRequestimportURL 做同样的事情 - 它只是检查 URL 是否正确形成。唯一的区别是它通过使用 MonadThrow 中的 throwM 抛出错误来发出错误信号。此外,如果你想使用 http-conduit,你无论如何都需要使用 parseRequest 来获得 Request,所以 importURL 根本没有意义。

如果您担心格式错误 URL,您可以确保将代码包装在 catch.

import Network.Connection (TLSSettings (..))
import Network.HTTP.Conduit

main :: IO ()
main = catch (do
                request <- parseRequest "https://github.com/"
                let settings = mkManagerSettings (TLSSettingsSimple True False False) Nothing
                manager <- newManager settings
                res <- httpLbs request manager
                print res)
             (\e -> case e of
                      HttpExceptionRequest{} -> putStrLn "Http request failed"
                      InvalidUrlException{} -> putStrLn "Bad URL")