Live data from Hacker News

Better HTTP server routing in Go 1.22

eli.thegreenplace.net

201–210 of 236 posts

Re: Better HTTP server routing in Go 1.22

#201

Earlier quoted context omitted.

Controller style declaration is often an overkill, it's also not exactly friendly to AOT. I personally prefer using MinApi style of declaration (first example). It can do request validation, model binding and inject services too: app.MapGet("/", async ( [FromBody] User user, [FromServices] MyService service) => { await service.Handle(user); return Results.Ok(); });

I might consider this for a very small service, but not for anything with a lot of controllers or services. When you get to the point where your backend is managing several workflows with dozens of views, dozens of models, and each with its own service, it becomes nice to be able to put these things in classes. I also dislike functions and controllers without explicitly defined return types.

You can write a method that uses IEndpointRouteBuilder for creating a route group so that you can split up your routes by object domain or into separate classes or however you want to.

Then you add an extensions method for WebApplication that registers all of your route groups when configuring your app pipeline. Each time you create a new route group, you add that group to your WebApplication extension method and it's wired up.

It's a little extra work up-front when you're first creating your app, but it also results in a clearer structure for how routes are defined once the app grows into the size you're describing.

Re: Better HTTP server routing in Go 1.22

#202

Earlier quoted context omitted.

I genuinely have no idea what you're referring to by "stringly-typing all over stdlib". I've written Go every day for the better part of a decade and used the standard library the whole time. What standard library functions require passing in the string of a type?

You are right, stdlib doesn't have much of stringly-typing. However the core language way of dealing with enums for example is extremely weak. It's common to have a typed enum on a struct. When parsing the struct, random string (or whatever the alias is) values sneak in and the only thing you can do is validate.

We're literally in the comment thread of a new stringly typed thing being introduced to the stdlib!

Re: Better HTTP server routing in Go 1.22

#203

Earlier quoted context omitted.

The `/foo/bar` and `/foo/*` use-case, where you want the first to go to a special page and the latter to go to some regular page. Perhaps the former is hard-coded/static and the latter looks up some URL parameter in a database. You can of course always implement this within `/foo/*`, but then you're implementing your own URL routing and working around the framework rather than working inside the framework. This feels…

This work, the panic is only triggered when the precedence rules can't fix a conflict. The behaviour documented here is actually extremely sensible and is best practices for anyone calling themselves a software engineer. Fail as early as possible, with as clear a message as possible.

But there is a precedence to pick for the panic case too.

Re: Better HTTP server routing in Go 1.22

#204

Earlier quoted context omitted.

How would you propose they block it? Can you provide an example from another language where it is blocked?

> How would you propose they block it? Rather than have a single global mux have a mux instance. You call methods on that instance to register routes and then serve the instance. This means you can use your ide to find all routes.

This already exists. The point is you can still call methods on your mux instance from anywhere in the codebase

Re: Better HTTP server routing in Go 1.22

#205

Earlier quoted context omitted.

You can't guarantee the order of registration will always be the same, so it's really undefined behavior. This is how the original ServeMux was designed and implemented, and they felt that was useful behavior to continue to support. From the design proposal[1]: > Using specificity for matching is easy to describe and > preserves the order-independence of the original ServeMux > patterns. But it can be hard to see at…

This seems like an issue caused by registration at a distance. I'm new to go and it is one of the things that felt most wrong. I'm used to a router where you define a big list of routes all in one place, maybe in some DSL. That's very easy to refactor, and doesn't have any ambiguity. If a library wants a route registered it puts a snippet in the docs for users to copy. Go in general seems to value clarity over magic…

How is this new to Go? Almost all Java frameworks that rely on Annotations do registration at a distance. Controllers are Routinely declared on different packages and even injected from dependency libraries.

Re: Better HTTP server routing in Go 1.22

#206

Forcing a panic when 2 routes are matched seems counter intuitive to me (versus, idk, every other web framework which uses the first-to-be-registered route that matches). Are there go-specific reasons for that? The edge case of "you might register HTTP routes in a bunch of places and it's harder to find that if multiple routes match" seems like something that be worked around with tooling. I've (ab)used the behavior…

> I've (ab)used the behavior of "first to be matched wins" a ton in my career

Even if the framework could guarantee the order of registration, this source-order heuristic is easy when you work alone or in a small team. Good luck guaranteeing order of routes in a large project with many teams.

Actually forcing you to not depend on things that can cause hidden bugs is a good design decision.

Re: Better HTTP server routing in Go 1.22

#207

Forcing a panic when 2 routes are matched seems counter intuitive to me (versus, idk, every other web framework which uses the first-to-be-registered route that matches). Are there go-specific reasons for that? The edge case of "you might register HTTP routes in a bunch of places and it's harder to find that if multiple routes match" seems like something that be worked around with tooling. I've (ab)used the behavior…

The panics are really annoying. Sometimes, you generate routes dynamically from some data, and it would be nice for this to be an error, so you can handle it yourself and decide to skip a route, or let the user know. With the panic, I have to write some spaghetti code with a recover in a goroutine.

> or let the user know

Many are misunderstanding when the panic happens. It does not happen when the user requests the path, it happens when the path is registered. The user will never arrive at that path to be notified. You will be notified that you have a logic error at the application startup. It can be caught by the simplest of tests before you deploy your application.

Re: Better HTTP server routing in Go 1.22

#208
post #179

Earlier quoted context omitted.

It's a programmer's error, so it should lead to a panic. There is simply no reason to ever handle this at runtime (as opposed to network errors or other things that can go wrong during normal operation and should be handled at runtime).

Not always, I can imagine a (weird, for sure) scenario where routes are a part of configuration, or are added dynamically at runtime. There’s `recover` of course, but I see no harm in returning an `error`, especially given that `errors.Join` is a thing now, so one doesn’t need to copy-paste ifs. The only reason not to is keeping the function signature intact.

And I see no Harm on making this extreme wierd usecase less user-friendly and force you to go through panic recover rather than force everyone else that will never get errors to handle errors.

Re: Better HTTP server routing in Go 1.22

#209
post #118

Earlier quoted context omitted.

It’s not an exception. There is no magic. Large code bases are large, and not every program is your simple crud app with a handful of endpoints that can be meaningfully managed a single function.

Hard panic in a "not simple app with large codebase" app because you couldn't match a route is amateur hour.

Registering handlers is a startup activity ... Practically the only time I allow a panic in my code. I also have unit tests for the entire startup sequence so theoretically code with ambiguous handlers would never be committed.

Re: Better HTTP server routing in Go 1.22

#210
post #13

Earlier quoted context omitted.

> Are there go-specific reasons for that? In general , the reason you use a compiled / typed language like golang at all (instead of, say, perl) is to "left shift" your bugs: A bug caught when you first spin up your application is better than a bug caught after a corner case acts up in the wild, and a bug caught when you compile is better than a bug caught when you first spin up your application. I recently ran a cro…

Wouldn’t the “left-shift” be a compiler error instead of a panic?

Ideally.

But that's impractical, so the next soonest time is at startup.

Post reply on HN