The default leaves you room to do harder things like custom chunk streaming.
The problem with Go’s default HTTP handlers
21–30 of 97 posts
Re: The problem with Go’s default HTTP handlers
#22Another possible extension of that design is using error types for HTTP codes. Something like: type HTTPError struct { Err error Code int } And then, with a couple of wrappers like this: func errorNotFound(err error) (wrapped error) { return &HTTPError{ Err: err, Code: http.StatusNotFound, } } you could do something like this: return errorNotFound(err) or this: return errorInternal(err) // 500 Internal Server Error
It's like 5 minute's work to code up, and I get to set whatever logging destination and redirect logic works best for my project.
Re: The problem with Go’s default HTTP handlers
#23This is such a tiny problem to quibble about. Just create a helper function that indeed force you to return values. The default leaves you room to do harder things like custom chunk streaming.
Re: The problem with Go’s default HTTP handlers
#24Earlier quoted context omitted.
"In the HTTP handlers it makes sense that you don't have return values, because: What would you do with that value exactly?" I agree that the baseline net/http implementation is correct. There is no appropriate default error (return) handler that the framework could implement, at the level it lives at, that would be correct and wouldn't be limiting. However I very often in my own Go code immediately create an abstrac…
"net/http" doesn't seem to come with a lot of great defaults that almost every web app would use such as named path parameters which you'd need to write a bunch of code and regex to extract, middleware, routing based on HTTP method etc. This is all stuff you'd have to implement anyway I'd much prefer using something that's an abstraction of it like Gin, Chi, Echo and the like. They're fairly performative and add very…
Re: The problem with Go’s default HTTP handlers
#25I agree, I wouldn't have been against a required return, but the mitigation is relatively straightforward by using custom handlers like mentioned at the end, if needed. I really like Go's default HTTP handlers because it offers that straightforward flexibility. Nothing rocket-sciencey.
Re: The problem with Go’s default HTTP handlers
#26The proposed improvement makes some ways to accidentally not adhere to the contract more obvious to humans, but still doesn’t enforce the contract.
I wonder whether there are languages that allow one to write a library that enforces such contracts at compile time.
(One way to do that would be to pass the Request and the ResponseWriter together with a state machine that is hard-coded in the API as a single object to the handler, with the equivalents of w.Write and http.Error declaring their state transitions, and having a language functionality that requires such object to be in an end state of that state machine whenever the function returns)
Re: The problem with Go’s default HTTP handlers
#27In Go you could write a function taking the HTTP client reference and setting it to null. This would at least produce a runtime error if it ever comes across the path rather than doing something else that is unexpected.
Re: The problem with Go’s default HTTP handlers
#28Streamed-response use cases get weird/complicated if you have to return the response.
(As a library, though, you'd also want to make the simple non-streaming case of "just return this blob of bytes" simple for the consumer, too.)
Re: The problem with Go’s default HTTP handlers
#29I agree, I wouldn't have been against a required return, but the mitigation is relatively straightforward by using custom handlers like mentioned at the end, if needed. I really like Go's default HTTP handlers because it offers that straightforward flexibility. Nothing rocket-sciencey.
Why not implement ServeHTTP on the custom handler though? That's not exactly difficult:
type MyHandlerFunc func(w http.ResponseWriter, r *http.Request) error
func (f MyHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := f(w, r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
Then you can just register it with http.Handle("/hello", MyHandlerFunc(someHandler))Re: The problem with Go’s default HTTP handlers
#30It's not something I even thought about, but I guess I see the point, I just don't agree. In the HTTP handlers it makes sense that you don't have return values, because: What would you do with that value exactly? The HTTP handler should always ensure that "something" is returned, at the very least a return code. Once something has been returned to the client, can you really argue that there's an error? There's a poin…
I think that approach used by clojure's ring shows an elegant way to represent http responses https://github.com/ring-clojure/ring/wiki/Concepts#responses. They are essentially structs with the following fields:
status := number
headers := map of string->string
body := stream | string | seq | inputstream
Request handlers are handed a request struct that is similar. The handler is a function that maps a request to a response (it doesn't actually write to streams itself).
I like this style for an http library for a couple of reasons:
1. HTTP resources can be viewed as functions whose domain is the request, and range is the response. Having the abstraction match that makes for really nice code. 2. If you model the request/response structs symmetrically and expose your http client as a handler itself, you can write proxies very easily. For an example of this, see http4k (https://www.http4k.org/documentation/) (a kotlin library).
I won't argue that the net/http abstraction should change, but I do agree with the author's take on the desired shape of an http library. I'd probably just write my own abstraction on top to fill the gap.