Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 正确处理WebException?_F#_Functional Programming_Purely Functional - Fatal编程技术网

F# 正确处理WebException?

F# 正确处理WebException?,f#,functional-programming,purely-functional,F#,Functional Programming,Purely Functional,我有以下F#程序,可从internet检索网页: open System.Net [<EntryPoint>] let main argv = let mutable pageData : byte[] = [| |] let fullURI = "http://www.badaddress.xyz" let wc = new WebClient() try pageData <- wc.DownloadData(fullU

我有以下F#程序,可从internet检索网页:

open System.Net

[<EntryPoint>]
let main argv = 
    let mutable pageData : byte[] = [| |]
    let fullURI = "http://www.badaddress.xyz"
    let wc = new WebClient()
    try
        pageData  <- wc.DownloadData(fullURI)
        ()
    with
    | :? System.Net.WebException as err -> printfn "Web error: \n%s" err.Message
    |  exn -> printfn "Unknown exception:\n%s" exn.Message

    0 // return an integer exit code
开放系统.Net
[]
让主argv=
让可变页面数据:字节[]=[| |]
让fullURI=”http://www.badaddress.xyz"
让wc=newwebclient()
尝试
pageData printfn“Web错误:\n%s”错误消息
|exn->printfn“未知异常:\n%s”exn.Message
0//返回整数退出代码
如果URI有效,并且计算机具有互联网连接,并且web服务器正确响应等,则此功能正常运行。在理想的函数编程环境中,函数的结果不会依赖于未作为参数传递的外部变量(副作用)

我想知道的是,什么是适当的F#设计模式来处理可能需要函数来处理可恢复的外部错误的操作。例如,如果网站关闭,可能需要等待5分钟,然后重试。是否应明确传递重试次数和重试之间的延迟等参数,还是可以将这些变量嵌入函数?

在F#中,当您想要处理可恢复的错误时,您几乎普遍希望使用
选项或
选项
类型。实际上,它们之间的唯一区别是
Choice
允许您返回有关错误的一些信息,而
option
则不允许。换句话说,
选项
是最好的选择,当它不关心某件事情如何或为什么失败时(只是它确实失败了)<代码>选项
用于了解失败的方式或原因的重要信息。例如,您可能希望将错误信息写入日志;或者,您可能希望根据失败的原因以不同的方式处理错误情况——这方面的一个很好的用例是提供准确的错误消息来帮助用户诊断问题

考虑到这一点,以下是我如何重构您的代码,以干净、功能性的方式处理故障:

open System
open System.Net

/// Retrieves the content at the given URI.
let retrievePage (client : WebClient) (uri : Uri) =
    // Preconditions
    checkNonNull "uri" uri
    if not <| uri.IsAbsoluteUri then
        invalidArg "uri" "The URI must be an absolute URI."

    try
        // If the data is retrieved successfully, return it.
        client.DownloadData uri
        |> Choice1Of2
    with
    | :? System.Net.WebException as webExn ->
        // Return the URI and WebException so they can be used to diagnose the problem.
        Choice2Of2 (uri, webExn)
    | _ ->
        // Reraise any other exceptions -- we don't want to handle them here.
       reraise ()

/// Retrieves the content at the given URI.
/// If a WebException is raised when retrieving the content, the request
/// will be retried up to a specified number of times.
let rec retrievePageRetry (retryWaitTime : TimeSpan) remainingRetries (client : WebClient) (uri : Uri) =
    // Preconditions
    checkNonNull "uri" uri
    if not <| uri.IsAbsoluteUri then
        invalidArg "uri" "The URI must be an absolute URI."
    elif remainingRetries = 0u then
        invalidArg "remainingRetries" "The number of retries must be greater than zero (0)."

    // Try to retrieve the page.
    match retrievePage client uri with
    | Choice1Of2 _ as result ->
        // Successfully retrieved the page. Return the result.
        result
    | Choice2Of2 _ as error ->
        // Decrement the number of retries.
        let retries = remainingRetries - 1u

        // If there are no retries left, return the error along with the URI
        // for diagnostic purposes; otherwise, wait a bit and try again.
        if retries = 0u then error
        else
            // NOTE : If this is modified to use 'async', you MUST
            // change this to use 'Async.Sleep' here instead!
            System.Threading.Thread.Sleep retryWaitTime

            // Try retrieving the page again.
            retrievePageRetry retryWaitTime retries client uri

[<EntryPoint>]
let main argv =
    /// WebClient used for retrieving content.
    use wc = new WebClient ()

    /// The amount of time to wait before re-attempting to fetch a page.
    let retryWaitTime = TimeSpan.FromSeconds 2.0

    /// The maximum number of times we'll try to fetch each page.
    let maxPageRetries = 3u

    /// The URI to fetch.
    let fullURI = Uri ("http://www.badaddress.xyz", UriKind.Absolute)

    // Fetch the page data.
    match retrievePageRetry retryWaitTime maxPageRetries wc fullURI with
    | Choice1Of2 pageData ->
        printfn "Retrieved %u bytes from: %O" (Array.length pageData) fullURI

        0   // Success
    | Choice2Of2 (uri, error) ->
        printfn "Unable to retrieve the content from: %O" uri
        printfn "HTTP Status: (%i) %O" (int error.Status) error.Status
        printfn "Message: %s" error.Message

        1   // Failure
开放系统
开放系统.Net
///检索给定URI处的内容。
let retrievePage(客户端:WebClient)(uri:uri)=
//前提条件
checkNonNull“uri”uri
如果没有选择1of2
具有
| :? System.Net.WebException作为webExn->
//返回URI和WebException,以便用于诊断问题。
Choice2Of2(uri,webExn)
| _ ->
//重新释放任何其他异常--我们不想在这里处理它们。
重放()
///检索给定URI处的内容。
///如果检索内容时引发WebException,则请求
///将重试指定次数。
让rec retrievePageRetry(retryWaitTime:TimeSpan)保留重试(客户端:WebClient)(uri:uri)=
//前提条件
checkNonNull“uri”uri
如果不是
//已成功检索该页。返回结果。
结果
|选择2of2作为错误->
//减少重试次数。
让重试次数=剩余重试次数-1u
//如果没有重试,则返回错误和URI
//用于诊断目的;否则,请稍等,然后重试。
如果重试次数=0u,则返回错误
其他的
//注意:如果修改为使用“异步”,则必须
//将此更改为在此处使用“Async.Sleep”!
System.Threading.Thread.Sleep retryWaitTime
//请再次尝试检索该页。
retrievePageRetry retryWaitTime重试客户端uri
[]
让主argv=
///用于检索内容的WebClient。
使用wc=新的WebClient()
///重新尝试获取页面之前等待的时间量。
让retryWaitTime=TimeSpan.FromSeconds 2.0
///我们将尝试获取每个页面的最大次数。
设maxPageRetries=3u
///要获取的URI。
让fullURI=Uri(“http://www.badaddress.xyz“,UriKind.Absolute)
//获取页面数据。
将retrievePageRetry retryWaitTime maxPageRetries wc fullURI与匹配
|选择1OF2页面数据->
printfn“从以下位置检索到%u字节:%O”(Array.length pageData)fullURI
0//成功
|Choice2Of2(uri,错误)->
printfn“无法从以下uri检索内容:%O”
printfn“HTTP状态:(%i)%O”(int error.Status)error.Status
printfn“消息:%s”错误。消息
1//失败
基本上,我将代码分为两个函数,再加上原始的
main

  • 尝试从指定URI检索内容的函数
  • 一个函数,包含重试尝试的逻辑;这“包装”了执行实际请求的第一个函数
  • 原来的main函数现在只处理“设置”(您可以轻松地从
    app.config
    web.config
    )和打印最终结果。换句话说,它忽略了重试逻辑——您可以使用
    match
    语句修改单行代码,并在需要时使用非重试请求函数
如果要从多个URI提取内容并在重试之间等待相当长的时间(例如5分钟),则应修改重试逻辑以使用优先级队列或其他内容,而不是使用
Thread.Sleep
Async.Sleep

无耻的插件:我的库中包含了一些东西,在构建这样的东西时,可以让您的生活变得更加轻松,特别是如果您想让所有东西都异步的话。最重要的是,它提供了一个工作流程和服务

至于您关于传入参数(如重试超时和重试次数)的问题,我认为没有一个硬性的规则来决定是传入参数还是在函数中硬编码参数。在大多数情况下,我更愿意把它们传下去,不过如果你有超过几个p