spring-hateoas icon indicating copy to clipboard operation
spring-hateoas copied to clipboard

Creating templated links when pointing to controller methods in Kotlin

Open Konngitas opened this issue 5 years ago • 7 comments

Currently, if we want a templated URI that we do not manually build ourselves, we can do linkTo(methodOn(UserController::class.java).getUser(null)).withRel("user")

This works fine with Java as we can pass null parameters to any method, but it does not work well in Kotlin, as in Kotlin we have to explicitly make our method's parameters nullable. This creates opportunities for mistakes and undermines the inherent safety of kotlin, making us do null checks just like in java instead of being able to assure that this controller method can not accept nullable parameters.

It would be nice if we had some other way to do this that isnt building the URI manually.

Konngitas avatar Jan 22 '20 13:01 Konngitas

I’m not sure what would work. We literally invoke the method but use a proxy to trap the results. This gives us the Method object to reflectively glean information. What happens if you try a concrete “fake” value?

gregturn avatar Apr 30 '20 14:04 gregturn

I also have this problem right now. If I use a fake value it gets populated and the link is not templated anymore. I even tried things like "{myParam}" but this gets (correctly) escaped.

otbe avatar Apr 30 '20 17:04 otbe

This behavior can be greatly improved by using Kotlin's really great reflection and introspection capabilities.

I think something like linkTo(function: KFunction<*>, vararg params: Any?) is a solution here. Call it like this linkTo(FooController::getFoos) or linkTo(FooController::getFoo, foo.id).

As far as I can tell though, there's no way to further optimize this and it will require runtime-validation to ensure that your params don't break null-safety.

I'm also kinda unsure what the original problem is. It seems that methodOn<T> will pass a T to the block--which means that inner proxy of T should also meet all of the null-safety constraints of the actual T.

sgarfinkel avatar May 01 '20 22:05 sgarfinkel

A somewhat null-safe way to achieve the above is already available - unfortunately it doesn't work with partially-filled templates. A syntax that would work with this lib is: linkTo(UserController::getUser.javaMethod!!).withRel("user").

Unfortunately, this results in an exception as template parameters are missing, caused by: https://github.com/spring-projects/spring-hateoas/blob/646e73c8c5ca5f1302bd6722e0ad338be0309fc9/src/main/java/org/springframework/hateoas/server/mvc/WebMvcLinkBuilder.java#L139 . Using methodOn instead results in a call to UriComponentsBuilder.buildAndExpand in https://github.com/spring-projects/spring-hateoas/blob/646e73c8c5ca5f1302bd6722e0ad338be0309fc9/src/main/java/org/springframework/hateoas/server/core/WebHandler.java#L151 which, with the setup that was done before, can handle null parameters.

Is there a way to unify the behaviour of these to components, ideally applying the one of WebHandler, so that type-safe link creation with templates in Kotlin are possible?

dirkbolte avatar Aug 18 '20 11:08 dirkbolte

Anyone looked into this issue recently?

larsw avatar Jun 24 '21 12:06 larsw

I am curious as well if this has any updates.

JogoShugh avatar Nov 19 '21 20:11 JogoShugh

A workaround for the time being is a utils method: fun <T> nullType(): T = null as T Providing null as a non-nullable type.

tleipzig avatar Jun 28 '23 12:06 tleipzig