3

我正在实现一个 F#-ish Web 请求函数,如下所示:

let request 
        (httpMethod:string) 
        (url:string) 
        (headers:Header list) 
        (content:Content option)=

    let groupHeaders (headers:WebHeaderCollection) =
        let d = new Dictionary<string, string list> ()
        let addToDict h =
            let oldValue = if d.ContainsKey(h) then d.Item(h) else []
            d.Item(h) <- oldValue @ [headers.Get h]

        headers.AllKeys |> Array.iter addToDict

        d |> Seq.map (|KeyValue|) |> Map.ofSeq

    async {
        let rq = WebRequest.Create(url) :?> HttpWebRequest
        rq.Method <- httpMethod

        headers 
        |> List.iter (fun (key, value) -> rq.Headers.Add(key, value) |> ignore ) 

        match content with
        | Some (contentType, bytes) ->
            rq.ContentType <- contentType
            do! rq.GetRequestStream().AsyncWrite(bytes) 
        | None -> ()

        try
            use! response = rq.AsyncGetResponse()
            let webResponse = response :?> HttpWebResponse

            let responseCode = webResponse.StatusCode

            let stream = webResponse.GetResponseStream()
            let length = webResponse.ContentLength |> int32 //TODO what if the data is bigger then 4GB?

            let! bytes = stream.AsyncRead(length)

            let respHeaders = groupHeaders webResponse.Headers
            return
                match webResponse.StatusCode with 
                | HttpStatusCode.OK -> OK (respHeaders, bytes)
                | HttpStatusCode.NotFound -> NotFound
                | otherCode -> Error <| otherCode.ToString()
        with 
            | :? WebException as ex -> 
                return Error <| ex.Status.ToString()
    }

问题是,如果我尝试获取一个页面(尝试获取一个riak值),它返回404的结果是WebException带有消息The remote server returned an error: (404) Not Found.而不是响应HttpStatusCode.NotFound

对相同的 URL执行curl GET操作会给我以下结果:

$ curl -v http://localhost:18098/riak/user/asfasfd?returnbody=true
* About to connect() to localhost port 18098 (#0)
*   Trying ::1...
* connected
* Connected to localhost (::1) port 18098 (#0)
> GET /riak/user/asfasfd?returnbody=true HTTP/1.1
> User-Agent: curl/7.27.0
> Host: localhost:18098
> Accept: */*
>
* additional stuff not fine /usr/src/ports/curl/curl-7.27.0-1/src/curl-7.27.0/lib/transfer.c:1037: 0 0
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 404 Object Not Found
< Server: MochiWeb/1.1 WebMachine/1.9.0 (someone had painted it blue)
< Date: Sun, 28 Oct 2012 05:30:12 GMT
< Content-Type: text/plain
< Content-Length: 10
<
not found
* Connection #0 to host localhost left intact
* Closing connection #0

任何想法为什么WebException抛出异常?如果我想识别 404-page not found 的情况,除了解析异常消息,我有什么选择?

4

2 回答 2

3

@desco 是正确的,但要详细说明他的答案——这是使用模式匹配解决问题的方法:

try
    // Do stuff here
    //
with
| :? WebException as webEx when (webEx.Response :? HttpWebResponse) ->
    /// The exception's Response, as an HttpWebResponse.
    /// From this we can get the HTTP status code of the response.
    let httpWebResponse = webEx.Response :?> HttpWebResponse

    // Return an error message based on the HTTP status code.
    match httpWebResponse.StatusCode with
    | HttpStatusCode.NotFound ->
        return NotFound

    | otherCode ->
        return Error <| otherCode.ToString()

| :? WebException as webEx ->
        return Error <| webEx.Status.ToString()

此外,您获得 WebException 的原因是因为这就是HttpWebResponse处理 4xx 和 5xx 响应代码的方式。match您检查的代码中的语句webResponse.StatusCode永远不会到达这种HttpStatusCode.NotFound情况,因为将引发异常。你应该保留在match那里,以处理任何不是的非错误代码HttpStatusCode.OK(例如,301 重定向)。

于 2012-10-28T17:03:16.273 回答
1

WebException 有 2 个属性可以帮助您:

  1. WebExceptionStatus类型的状态。404 应由 ProtocolError 指示
  2. Response暴露远程主机返回的响应。对 HttpWebResponse 的向下转换响应及其使用Status属性
于 2012-10-28T08:22:15.133 回答