fsprojects / FSharp.Data

F# Data: Library for Data Access
https://fsprojects.github.io/FSharp.Data
Other
817 stars 288 forks source link

How to extract the status code from a WebException generated by XmlProvider<...>.Load ? #1002

Open dgellow opened 8 years ago

dgellow commented 8 years ago

Hi,

I'm using XmlProvider to fetch and parse RSS feeds. I want to handle HTTP Errors but I cannot find a way to get the status code of a webexception.Response. The C# approach is to cast the WebResponse to HttpWebResponse, that doesn't seem to work with the response from MyXml.Load.

To be more explicit, look at the code below. downcastWebResponse tries to downcast the WebResponse from the exception into an HttpWebResponse. Unfortunately it fails thus returns None. Without the guard, if I do something like let resp = e.Response :?> HttpWebResponse I get the error Unhandled Exception: System.InvalidCastException: Specified cast is not valid..

So, my question is: How can I get the status code from the exception response? And if I have to cast to HttpWebResponse, how can I do it?

open System
open System.Net
open FSharp.Data

type Rss = XmlProvider<sampleRssFile>
let feedWith404 = "http://feeds.reuters.com/reuters/blogs/the-human-impact"

let downcastWebResponse (wr: WebResponse) =
    match wr with
    | :? HttpWebResponse as hwr -> Some(hwr.StatusCode)
    | _ -> None

let loadRssIndex (url: string) : Choice<Rss.Rss, RssException> =
    try
        Choice1Of2 (Rss.Load(url))
    with
    | :? WebException as e ->
        match e.Status with
        | WebExceptionStatus.ProtocolError ->
            let statusCode = downcastWebResponse e.Response // <== try to downcast
            match statusCode with
            | Some status ->
                match status with
                | HttpStatusCode.NotFound ->
                    Choice2Of2 (RssException.IndexNotFound e)
                | _ -> reraise()
            | None -> reraise() // <== downcast failed
        | _ -> reraise()

// Run
loadRssIndex feedWith404
vdehaven commented 7 years ago

The HttpWebResponse is sitting on e.Response.res, a private member of Response on WebException. It's not only private, it's protected or internal, so we can't expose it in a subclass. Would love to have access to it, as mapping a WebResponse to an HttpWebResponseusing the public members is error-prone at best.

YoRyan commented 10 months ago

This issue should remain open. So far as I can tell, there is still no convenient way to retrieve the HTTP status code of a WebException. The least-worst way is to extract it from e.Message, which is a string.

This is a major pitfall. If you Google for C# solutions, they'll tell you to downcast to HttpWebResponse, which does not work for FSharp.Data's WebResponse.

dgellow commented 9 months ago

Not sure why it was closed, I don’t remember closing it? Pretty I didn’t interact with this issue since I opened it in 2016. Anyway, it’s reopened.

kirse commented 4 days ago

Confirming this is still an issue. Currently handling it like so:

try
    // whatever logic
with
| :? Net.WebException as ex ->
    if ex.Message.Contains("(404) Not Found") then // more logic