I'm not sure exactly how to phrase this question, and I've seen others ask similar but not really come up with answers (which tells me I'm asking the wrong question, but I'm not sure how else to approach this).
I'm trying to learn some basic Go, and I've come unstuck at the first hurdle.
In my test code, I'm doing a basic http GET to a domain that doesn't exist to trigger a DNS warning. I worked out that err.error() returns a string, so to assert whether it was a DNS error, I used string comparison:
resp, err := http.Get(link)
if err != nil {
if strings.Contains(err.Error(), "no such host") == true {
return "no such host"
}
}
This is obviously hacky, so I did some googling to see if there is a better way to work out what kind of error was raised, and I found the following SO answer:
How can I check specific golang net/http error code?
Package "errors" has functions As, Is to unwrap specific error types, and package "net" has a *DNSError type. So:
var dnsErr *net.DNSError
if errors.As(err, &dnsErr) {
...
}
This code works, but I have absolutely zero idea how this conclusion was reached. Below is how I'm approaching trying to understand this, and I'd like to know where I'm going wrong. I understand (vaguely) what .Is() and .As() are doing, but what I don't understand is how to work out what error "type" to provide those functions without guessing or prior knowledge.
I looked at the client.get() documentation here which says:
Any returned error will be of type *url.Error.
some more googling and I found that I need to cast(?) the error to that type to work with it:
urlErr := err.(*url.Error)
the *url.Error contains:
&url.Error{Op:"Get", URL:"http://doesnotexistkjdfhgsdfsdf.com", Err:(*net.OpError)(0xc00021a2d0)}
so I then look at the net.OpError contained in the url.Error:
netOpError := urlErr.Err.(*net.OpError)
fmt.Printf("Net Op Error contains: %#v\n", netOpError)
---
Net Op Error contains: &net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:net.Addr(nil), Err:(*net.DNSError)(0xc0001a0040)}
I then do the same thing and "unpack" the net.DNSError contained within net.OpError:
dnsError := netOpError.Err.(*net.DNSError)
fmt.Printf("DNSError contains: %#v\n", dnsError)
---
DNSError contains: &net.DNSError{Err:"dial udp 169.254.169.254:53: connect: no route to host", Name:"doesnotexistkjdfhgsdfsdf.com", Server:"169.254.169.254:53", IsTimeout:false, IsTemporary:true, IsNotFound:false}
The net.DNSError doesn't "contain" any other errors so to me, this suggests it's the bottom of the chain and the "real" error (or, at least, one I wanted to work with).
Thing is, this is not a viable approach, and I don't understand how we're supposed to approach this. Before the initial SO article I found, I had no idea that net.DNSError is a thing, or that my error could be of that "type".
If you didn't know a particular error type exists, and that a function call could possibly be of that type, how would you know?
I have a very limited understanding of interfaces and types in general in Go, which I'm sure isn't helping here, but to me there seems to be a huge leap between having an error and knowing what kind of error to check it could be. I hope this question makes sense!