使用Golang的另一个函数返回值?

时间:2016-04-09 14:21:18

标签: function go error-handling

在Go中,是否可以仅使用函数返回?

例如,我想检查错误而不是重复if语句,我只想调用checkErrors(err)之类的函数。

以下是一个示例(goplayground

func foobar(str string) err {
    _, err := ioutil.ReadAll(resp.Body)
    checkError(err) // If error != nil then foobar will return the err
    /*if err != nil {
        return err
    }*/
}

func checkError(err error) {
    if err != nil {
        // Then make the function this is being called within return the err.
    }
}

2 个答案:

答案 0 :(得分:1)

您的导师是正确的:不要重复代码。它被称为DRY principal

但Go的作者在设计语言时采取了不同的方向。它破坏了发展社区,引发了战争,它扭曲了空间和时间。

其中一个方向是接受一点点复制,以使代码更容易阅读。正如您所注意到的,错误处理就是这样一个区域。它会在你的应用程序中邀请一个小的重复模式来检查nil,而不是抽象掉(并且很容易忽略)你应该做的真正的错误处理。

Rob Pike在GoLang演讲的设计(和哲学)中谈到了这个方向:

http://talks.golang.org/2012/splash.article

  

如果错误使用特殊控制结构,则错误处理会扭曲处理错误的程序的控制流。 try-catch-finally块的类似Java的样式交织了多个重叠的控制流,这些控制流以复杂的方式进行交互。虽然相比之下,Go使检查错误更加冗长,但显式设计使控制流程直截了当 - 字面上。

不是每个人都同意这也是战争开始的原因。

但是,如果对开发人员造成负担,这些决定会撼动行业标准,这正是他们这样做的原因:您每次都被迫处理前面和中心的每个错误。你不能忽视或让其他东西来处理它。

并非所有人都同意。

我强烈建议你和你的图图尔阅读有关Go设计的帖子。然后你们两个可以做出决定而不是Go是的方式。

答案 1 :(得分:1)

只是为了添加已经很好的答案,您可以使PATH = Dir.pwd VERSION = Whitewidow.version SEARCH = File.readlines("#{PATH}/lib/search_query.txt").sample info = YAML.load_file("#{PATH}/lib/rand-agents.yaml") @user_agent = info['user_agents'][info.keys.sample] OPTIONS = Struct.new(:default, :file, :example) def usage_page Format.usage("You can run me with the following flags: #{File.basename(__FILE__)} -[d|e|h] -[f] <path/to/file/if/any>") exit end def examples_page Format.usage('This is my examples page, I\'ll show you a few examples of how to get me to do what you want.') Format.usage('Running me with a file: whitewidow.rb -f <path/to/file> keep the file inside of one of my directories.') Format.usage('Running me default, if you don\'t want to use a file, because you don\'t think I can handle it, or for whatever reason, you can run me default by passing the Default flag: whitewidow.rb -d this will allow me to scrape Google for some SQL vuln sites, no guarentees though!') Format.usage('Running me with my Usage flag will show you the boring usage page.. Yeah it\'s not very exciting..') end class OptParser def self.parse_argv(options) args = OPTIONS.new opt_parser = OptionParser.new do |opts| opts.banner = usage_page opts.on('-d', '--default', 'Run me in default mode, I\'ll scrape Google for SQL vulns.') do |d| args[:default] = d end opts.on('-fFILE', '--file=FILE', 'Pass me the name of the file. Leave out the beginning forward slash. (/lib/ <= incorrect lib/ <=correct)') do |f| args.fileFILE = f end opts.on('-e', '--example', 'Shows my example page. It\'s really not that hard to figure out, but I\'m a nice albino widow.') do |e| args[:example] = e end opts.on('-h', '--help', 'Shows a complete list of all my usages, with what they do, and their secondary flag.') do puts opts exit end opts.on('-u', '--usage', 'Shows my usage page, a short list of possible flags, use the help flag (-h) for a more complete list.') do usage_page exit end end opt_parser.parse!(options) return args end end def pull_proxy info = parse("http://www.nntime.com/",'.odd', 1) @ip = info[/\D\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\D/].gsub(">", "").gsub("<", "") @port = info[/8080/] || info[/3128/] || info[/80/] || info[/3129/] || info[/6129/] proxy = "#{@ip}:#{@port}" Format.info("Proxy discovered: #{proxy}") end def page(site) Nokogiri::HTML(RestClient.get(site)) end def parse(site, tag, i) parsing = page(site) parsing.css(tag)[i].to_s end def get_urls if OPTIONS[:default] Format.info('I\'ll run in default mode then!') Format.info("I'm searching for possible SQL vulnerable sites, using search query #{SEARCH}") agent = Mechanize.new agent.user_agent = @user_agent page = agent.get('http://www.google.com/') google_form = page.form('f') google_form.q = "#{SEARCH}" url = agent.submit(google_form, google_form.buttons.first) url.links.each do |link| if link.href.to_s =~ /url.q/ str = link.href.to_s str_list = str.split(%r{=|&}) urls = str_list[1] next if urls.split('/')[2].start_with? 'stackoverflow.com', 'github.com', 'www.sa-k.net', 'yoursearch.me', 'search1.speedbit.com', 'duckfm.net', 'search.clearch.org', 'webcache.googleusercontent.com' next if urls.split('/')[1].start_with? 'ads/preferences?hl=en' urls_to_log = URI.decode(urls) Format.success("Site found: #{urls_to_log}") sleep(1) File.open("#{PATH}/tmp/SQL_sites_to_check.txt", 'a+') { |s| s.puts("#{urls_to_log}'") } end end Format.info("I've dumped possible vulnerable sites into #{PATH}/tmp/SQL_sites_to_check.txt") else begin_vulnerability_check end end def begin_vulnerability_check (OPTIONS.file) ? file = IO.read(ARGV[1]) : file = IO.read("#{PATH}/tmp/SQL_sites_to_check.txt") #if options.file file.each_line do |vuln| Format.info("Let's check this file out..") #IO.read("#{PATH}/#{ARGV[1]}").each_line do |vuln| begin Format.info("Parsing page for SQL syntax error: #{vuln.chomp}") Timeout::timeout(10) do begin if parse("#{vuln.chomp}'", 'html', 0)[/You have an error in your SQL syntax/] Format.success("URL: #{vuln.chomp} returned SQL syntax error, temporarily dumped to SQL_VULN.txt") File.open("#{PATH}/tmp/SQL_VULN.txt", "a+") { |s| s.puts(vuln) } sleep(1) else Format.warning("URL: #{vuln.chomp} is not vulnerable, dumped to non_exploitable.txt") File.open("#{PATH}/log/non_exploitable.txt", "a+") { |s| s.puts(vuln) } sleep(1) end rescue Timeout::Error, OpenSSL::SSL::SSLError Format.info("URL: #{vuln.chomp} failed to load dumped to non_exploitable.txt") File.open("#{PATH}/log/non_exploitable.txt", "a+") { |s| s.puts(vuln) } next sleep(1) end end rescue RestClient::ResourceNotFound, RestClient::InternalServerError, RestClient::RequestTimeout, RestClient::Gone, RestClient::SSLCertificateNotVerified, RestClient::Forbidden, OpenSSL::SSL::SSLError, Errno::ECONNREFUSED, URI::InvalidURIError, Errno::ECONNRESET, Timeout::Error, OpenSSL::SSL::SSLError, ArgumentError, RestClient::MultipleChoices, RestClient::Unauthorized, SocketError, RestClient::BadRequest, RestClient::ServerBrokeConnection => e Format.err("URL: #{vuln.chomp} failed due to an error while connecting, URL dumped to non_exploitable.txt") File.open("#{PATH}/log/non_exploitable.txt", "a+") { |s| s.puts(vuln) } next end end usage_page if OPTIONS.nil? end case OPTIONS when OPTIONS[:default] || OPTIONS.file begin Whitewidow.spider sleep(1) Credits.credits sleep(1) Legal.legal get_urls begin_vulnerability_check File.truncate("#{PATH}/tmp/SQL_sites_to_check.txt", 0) Format.info("I'm truncating SQL_sites_to_check file back to #{File.size("#{PATH}/tmp/SQL_sites_to_check.txt")}") Copy.file("#{PATH}/tmp/SQL_VULN.txt", "#{PATH}/log/SQL_VULN.LOG") File.truncate("#{PATH}/tmp/SQL_VULN.txt", 0) Format.info("I've run all my tests and queries, and logged all important information into #{PATH}/log/SQL_VULN.LOG") unless File.size("#{PATH}/log/SQL_VULN.LOG") == 0 rescue Mechanize::ResponseCodeError, RestClient::ServiceUnavailable, OpenSSL::SSL::SSLError, RestClient::BadGateway => e d = DateTime.now Format.fatal("Well this is pretty crappy.. I seem to have encountered a #{e} error. I'm gonna take the safe road and quit scanning before I break something. You can either try again, or manually delete the URL that caused the error.") File.open("#{PATH}/log/error_log.LOG", 'a+'){ |error| error.puts("[#{d.month}-#{d.day}-#{d.year} :: #{Time.now.strftime("%T")}]#{e}") } Format.info("I'll log the error inside of #{PATH}/log/error_log.LOG for further analysis.") end when OPTIONS[:example] examples_page else Format.info("You failed to pass me a flag, let's try again and pass a flag this time!") end OptParser.new OPTIONS = OptParser.parse_argv(ARGV) 功能更加自定义,这样您仍然可以预先处理错误,同时仍然保持干燥:

checkError

通过这种方式,您可以在保存两行重复错误处理代码的同时传递您希望与每个错误一起使用的处理函数,而不会忘记责任。

func checkError(err error, handler func(e error)) {
    if err != nil {
        handler(err)
    }
}