rut icon indicating copy to clipboard operation
rut copied to clipboard

Look up path parameters by name

Open bowbahdoe opened this issue 1 year ago • 4 comments
trafficstars

Maybe i'm missing something, but i don't see an easy way to get the value for a path parameter by name instead of by index

bowbahdoe avatar Nov 21 '24 05:11 bowbahdoe

Yeah there is no such method because the Result does not store captured parameters in a Map for performance reasons. Indeed it only stores a list of indexes into the original input string.

Here is a utility method one could use to get a parameter by name:

static Optional<CharSequence> paramValueByName(final Router.Result<?> result, final String paramName) {
  for (int i = 0; i < result.params(); i++) {
    if (result.paramName(i).equals(paramName)) {
      return Optional.of(result.paramValue(i));
    }
  }
  return Optional.empty();
}

Use it like this:

final Optional<CharSequence> user = paramValueByName(result, "user");

Arguably we could add this utility method to Result. It is however more efficient for users to store the indexes of parameters e.g. as constants and use those indexes to access the captured values.

danielnorberg avatar Nov 21 '24 07:11 danielnorberg

Yeah i'm less concerned with absolute maximal efficiency and more with the usage side. Considering routes tend to have relatively few route params I don't think you need a map. The linear scan approach is probably just fine.

The reason I am on this is I just made this library https://github.com/bowbahdoe/jdk-httpserver-rutrouter

void main() {
    var router = RutRouter.builder()
            .get("/hello/<name>", e -> {
                e.getResponseHeaders().put("Content-Type", List.of("text/html"));
                e.sendResponseHeaders(200, 0);
                var c = RutRouter.result(e);
                try (var body = e.getResponseBody()) {
                    body.write(("<h1>Hiya  " + c.paramValueDecoded(0) + "   " + c.query() + "</h1>").getBytes(StandardCharsets.UTF_8));
                }
            })
            .notFoundHandler(e -> {
                e.getResponseHeaders().put("Content-Type", List.of("text/html"));
                e.sendResponseHeaders(404, 0);
                try (var body = e.getResponseBody()) {
                    body.write("<h1>Not Found</h1>".getBytes(StandardCharsets.UTF_8));
                }
            })
            .build();

    var server = HttpServer.create(new InetSocketAddress(8783), 0);
    server.createContext("/", router);
    server.start();
}

As you can see from the example, .paramValueDecoded(0) really doesn't work that well if the route definition and handler are far apart in the code.

i'd prefer to use .paramValueDecoded("name")

bowbahdoe avatar Nov 21 '24 14:11 bowbahdoe

Yeah, linear scan is fine and more efficient than a map for most cases.

Sure, we can add paramValueByName and paramValueDecodedByName methods to Result. Would you like to submit a PR?

danielnorberg avatar Nov 22 '24 13:11 danielnorberg

Okay opened #21 - we can bikeshed there

bowbahdoe avatar Nov 22 '24 21:11 bowbahdoe

Would you mind publishing a release with the changes from #21 ?

bowbahdoe avatar Apr 03 '25 05:04 bowbahdoe