Repository navigation
Parameter lookup feature for Context #1968
Copy link
Copy link
Closed
Labels
Milestone
Description
In Jooby 1.x all types of parameters (query, path, etc..) could be acquired by calling the same method. This could led to some problems or confusion, which was nicely addressed by 2.x.
In some situations however it would be convenient to specify multiple places a parameter should be searched. I created a helper class for this, which can be called the following way:
final Value foo = ContextHelpers.lookup(ctx)
.inPath()
.inQuery()
.get("foo");Click to see the implementation.
public static ValueLookup lookup(Context context) {
return new ValueLookupImpl(context);
}
private static class ValueLookupImpl implements ValueLookup.Stack {
protected final Context context;
protected List<Function<String, Value>> chain = new ArrayList<>();
ValueLookupImpl(Context context) {
this.context = context;
}
@Override
public Stack inPath() {
chain.add(context::path);
return this;
}
@Override
public Stack inHeader() {
chain.add(context::header);
return this;
}
@Override
public Stack inCookie() {
chain.add(context::cookie);
return this;
}
@Override
public Stack inFlash() {
chain.add(context::flash);
return this;
}
@Override
public Stack inSession() {
chain.add(context::session);
return this;
}
@Override
public Stack inQuery() {
chain.add(context::query);
return this;
}
@Override
public Stack inForm() {
chain.add(context::form);
return this;
}
@Override
public Stack inMultipart() {
chain.add(context::multipart);
return this;
}
@Override
public Value get(String name) {
if (chain.isEmpty()) {
throw new IllegalStateException("Did you forget to call an in*() method?");
}
return chain.stream()
.map(f -> f.apply(name))
.filter(v -> !v.isMissing())
.findFirst()
.orElseGet(() -> Value.missing(name));
}
}
public interface ValueLookup {
Stack inPath();
Stack inHeader();
Stack inCookie();
Stack inFlash();
Stack inSession();
Stack inQuery();
Stack inForm();
Stack inMultipart();
interface Stack extends ValueLookup {
Value get(String name);
default Optional<ValueNode> getNode(String name) {
return Optional.of(get(name))
.map(v -> v instanceof ValueNode ? (ValueNode) v : null);
}
}
}If you would like to see something like this in Jooby, I could make a pull request. It could be used as a method of Context:
final Value foo = ctx.lookup()
.inPath()
.inQuery()
.get("foo");Reactions are currently unavailable
Activity
jknack commentedon Sep 2, 2020
What about
ctx.lookup(String, LookupScope...)where LookupScope (not super sure about the name) is an Enum?imeszaros commentedon Sep 2, 2020
The reason for this design was that it's very convenient to write, i mean it's a fluent interface, you immediately get assist form the IDE. I think using an enum would somewhat break this convenience.
What are your points, would you like to avoid additional interfaces?
jknack commentedon Sep 2, 2020
Good argument. What about MVC?
imeszaros commentedon Sep 2, 2020
Yes that's a good question.. I was thinking on an annotation where you can specify the sources like
@Param({ PATH, QUERY, ... })but I dropped the idea because you can simply have the context as a parameter and then you can start a lookup on it.jknack commentedon Sep 2, 2020
yea, that was the reason of suggesting an enum approach. To "mimic" the MVC usage.
Let's add it to Context for now as you suggested.
imeszaros commentedon Sep 2, 2020
I see your point.. what if we do both?
and in
Contextwe could add both:The later would simply call the former at the end..
jknack commentedon Sep 2, 2020
Go for it!