-
Notifications
You must be signed in to change notification settings - Fork 38.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Optional
with null-safe and Elvis operators in SpEL expressions
#20433
Comments
Juergen Hoeller commented Rob Winch, I figure this might have to be handled in Spring Security's authorization interceptor, specifically detecting an |
Rob Winch commented Thanks for the report Peter Luttrell
This does fail because
This is not true. In either case, the Juergen Hoeller I don't think it really makes sense to automatically unwrap Users can always do something like |
Mohamed Amine Mrad commented Hello, |
Stéphane Toussaint commented I have another use case for this feature request. I use Spring Integration and some of my service layer bean methods are now returning Optional generic types. This is a sample of two successive service-activator call, the payload is the Optional<Person> return from retrieveByUsername. <int:service-activator expression="@personService.retrieveByUsername(headers.username)" />
<int:service-activator expression="@personService.doWithPerson(payload)" /> The doWithPerson method has not changed ; still waiting for a Person. Class PersonService {
public void doWithPerson(Person person) {
...
}
} The retrieveByUsername however now returns an Optional of Person Class PersonService {
public Optional<Person> retrieveByUsername(String username) {
return Optional.of(...)
}
} Now Spring Integration (actually the Spring Expression Evaluator) complains it can't find the targeted method with a message like :
Will an OptionalToObjectConverter be feasible ? Maybe it can be possible to rely on Nullable annotation on the target method to handle the Optional.empty() case ?(return null or throw ?). |
Another use case is with Optional<Task> getTask(int taskId); The controller could check if the task is linked to the authorized user like this: @DeleteMapping("/{taskId}")
@PreAuthorize("@taskServiceImpl.getTask(#taskId).orElse(null)?.user.id == #userDetails.id")
public String destroy(@AuthenticationPrincipal ApplicationUserDetails userDetails,
@PathVariable("taskId") Integer taskId) {
return "redirect:/tasks";
} The |
My use case would be with
|
At the very least, |
This also applies to Thymeleaf templating, which uses SpEL in the default Boot configuration. |
+1 |
This is causing me so much trouble now! I created a custom company lib that would benefit a lot from SpEL, and this is essentially making it useless because a lot of our codebase has a bias towards using Optional. @jhoeller Why declined? Why not planned? This sounds like such a resonable request, so many people here voicing their support, and probably very natural do implement! |
Indeed. Lack of this caused us to drop SpEL long ago as the basis for something. |
Optional
in SpEL expressions
Optional
in SpEL expressionsOptional
with null-safe and Elvis operators in SpEL expressions
This commit is a Proof of Concept. See spring-projectsgh-20433
After further consideration, we have decided to introduce first-class support for The discussions in this issue have presented two separate use cases.
After experimentation, I have determined that supporting To address use case |
A proof of concept can be viewed in the following feature branch. main...sbrannen:spring-framework:issues/gh-20433-spel-null-safe-elvis-optional That allows the original use case: @PostAuthorize("canAccessOrganization(returnObject.organizationId)")
public Optional<Person> getPerson(String personId){
// ...
} ... to be replaced with: @PostAuthorize("canAccessOrganization(returnObject?.organizationId)")
public Optional<Person> getPerson(String personId){
// ...
} If If If @wimdeblauwe, you should then be able to rewrite your example as follows. @DeleteMapping("/{taskId}")
@PreAuthorize("@taskServiceImpl.getTask(#taskId)?.user?.id == #userDetails.id")
public String destroy(@AuthenticationPrincipal ApplicationUserDetails userDetails,
@PathVariable("taskId") Integer taskId) {
return "redirect:/tasks";
} |
Do you mean
Do you mean |
No, in that particular example
Yes, that would indeed be better. I was merely modifying the original example to make use of the proposed new feature. But I'll update that to use null-safe navigation throughout the compound expression. |
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - PropertyOrFieldReference Operators not yet supported: - MethodReference - Projection - Selection See spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - PropertyOrFieldReference TODO: support the following operators: - MethodReference - Projection - Selection TODO: update reference manual Closes spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - MethodReference - PropertyOrFieldReference TODO: support the following operators: - Projection - Selection TODO: update reference manual Closes spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - MethodReference - PropertyOrFieldReference - Projection TODOs: - Support the Selection operator. - Update the SpEL chapter of the reference manual. Closes spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - MethodReference - PropertyOrFieldReference - Projection - Selection TODO: Update the SpEL chapter of the reference manual. Closes spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - MethodReference - PropertyOrFieldReference - Projection - Selection TODO: Update the SpEL chapter of the reference manual. Closes spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - MethodReference - PropertyOrFieldReference - Projection - Selection TODO: Update the SpEL chapter of the reference manual. Closes spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - Elvis - Indexer - MethodReference - PropertyOrFieldReference - Projection - Selection TODO: Update the SpEL chapter of the reference manual. Closes spring-projectsgh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - PropertyOrFieldReference - MethodReference - Indexer - Projection - Selection - Elvis Specifically, when a null-safe operator is applied to an empty `Optional`, it will be treated as if the `Optional` were `null`, and the subsequent operation will evaluate to `null`. However, if a null-safe operator is applied to a non-empty `Optional`, the subsequent operation will be applied to the object contained in the `Optional`, thereby effectively unwrapping the `Optional`. For example, if `user` is of type `Optional<User>`, the expression `user?.name` will evaluate to `null` if `user` is either `null` or an empty `Optional` and will otherwise evaluate to the `name` of the `user`, effectively `user.get().getName()` for property access. Note, however, that invocations of methods defined in `Optional` are still supported on an empty `Optional`. For example, if `name` is of type `Optional<String>`, the expression `name?.orElse('Unknown')` will evaluate to `Unknown` if `name` is an empty `Optional` and will otherwise evaluate to the `String` contained in the `Optional` if `name` is a non-empty `Optional`, effectively `name.get()`. Closes spring-projectsgh-20433
This is a prerequisite for null-safe Optional support. See gh-20433
This is a prerequisite for null-safe Optional support. See gh-20433
This commit introduces null-safe support for java.util.Optional in the following SpEL operators: - PropertyOrFieldReference - MethodReference - Indexer - Projection - Selection - Elvis Specifically, when a null-safe operator is applied to an empty `Optional`, it will be treated as if the `Optional` were `null`, and the subsequent operation will evaluate to `null`. However, if a null-safe operator is applied to a non-empty `Optional`, the subsequent operation will be applied to the object contained in the `Optional`, thereby effectively unwrapping the `Optional`. For example, if `user` is of type `Optional<User>`, the expression `user?.name` will evaluate to `null` if `user` is either `null` or an empty `Optional` and will otherwise evaluate to the `name` of the `user`, effectively `user.get().getName()` for property access. Note, however, that invocations of methods defined in the `Optional` API are still supported on an empty `Optional`. For example, if `name` is of type `Optional<String>`, the expression `name?.orElse('Unknown')` will evaluate to "Unknown" if `name` is an empty `Optional` and will otherwise evaluate to the `String` contained in the `Optional` if `name` is a non-empty `Optional`, effectively `name.get()`. Closes spring-projectsgh-20433
This support will be available in the upcoming Spring Framework 7.0 M3 release. For details, check out the updated sections of the reference manual. Feedback is welcome! 😎 |
We have had an ObjectToOptionalConverter since Spring Framework 4.1; however, prior to this commit we did not have a standard Converter for the inverse (Optional to Object). To address that, this commit introduces an OptionalToObjectConverter that unwraps an Optional, using the ConversionService to convert the object contained in the Optional (potentially null) to the target type. This allows for conversions such as the following. - Optional.empty() -> null - Optional.of(42) with Integer target -> 42 - Optional.of(42) with String target -> "42" - Optional.of(42) with Optional<String> target -> Optional.of("42") The OptionalToObjectConverter is also registered by default in DefaultConversionService, alongside the existing ObjectToOptionalConverter. See gh-20433 Closes gh-34544
FYI: I introduced an @stoussaint, it would be great if you could give it a try to see if it addresses your needs. You can experiment with in 7.0 snapshots. |
Peter Luttrell opened SPR-15878 and commented
This is a feature request to add support for Java 8 Optionals to the Spring Expression Language.
One use case that I just ran into is wanting to use
@PostAuthorize
on a method that returns an Optional in conjunctions with custom expressions. For example the following fails:In this case, if the returned reference isn't present, that
@PostAuthorize
would allow the response, which should be Optional.empty(). If it is present, then it'd be dereferenced into the returnObject, so we'd have direct access to its fields for use in the expression.4 votes, 6 watchers
The text was updated successfully, but these errors were encountered: