1

When I try to render my view in Thymeleaf I get an error Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'currentTemperature' cannot be found on object of type 'reactor.core.publisher.MonoMapFuseable' - maybe not public or not valid?

The Spring WebFlux documentation states "model attributes that have a reactive type wrapper are resolved to their actual values", but passing a Mono<> to the view as a Model gives me the error above.

  @RequestMapping(path = "/")
  @GetMapping
  public String home(Model model) {
    Mono<ThermostatState> thermostatState = thermostatClient.fetchThermostatState();
    model.addAttribute("thermostatState", thermostatState);
    return "home";
  }

Blocking the Mono<> and unwrapping the internal value makes the template render unchanged, but kinda eliminates the point of using the reactive libraries.

  @RequestMapping(path = "/")
  @GetMapping
  public String home(Model model) {
    Mono<ThermostatState> thermostatState = thermostatClient.fetchThermostatState();
    ThermostatState unwrappedState = thermostatState.block();
    model.addAttribute("thermostatState", unwrappedState);
    return "home";
  }

The project is entirely dependent on the spring starter dependencies and doesn't have an explicit configuration class.

JoshRivers
  • 9,920
  • 8
  • 39
  • 39

2 Answers2

1

Hi I am also new to Spring and Reactive programming; but I think, this can be handled in this way.

 @RequestMapping(path = "/")
 @GetMapping
 public Mono<String> home(Model model) {
    return thermostatClient.fetchThermostatState()
      .map(thermostatState -> {
         model.addAttribute("thermostatState", thermostatState);
         return "home";
      });
 }
Krishna
  • 609
  • 1
  • 6
  • 17
  • You code works, and I think it is a better solution than using `block()`. I imagine your way doesn't occupy a thread where mine does (not sure if this is true, but it makes sense). But I feel like deferring the return from the controller until the future resolves shouldn't be necessary. The Thymeleaf Webflux samples don't do this https://github.com/thymeleaf/thymeleafsandbox-biglist-reactive/blob/3.0-spring5/src/main/java/thymeleafsandbox/biglistflux/web/controller/ThymeleafController.java but for some reason it doesn't work for me. – JoshRivers Apr 29 '20 at 20:12
  • BTW, the return value of the method needs to be changed from `String` to `Mono ` for the above code to work. – JoshRivers Apr 29 '20 at 20:12
  • 1
    Ya I missed ot change the return type and I corrected it. Feel good to see my answer is helpful to you. Please mark it as answer, if it is right solution to you, – Krishna Apr 30 '20 at 05:32
  • if it is working for you then you should accept this answer @JoshRivers – Shoshi Apr 30 '20 at 16:20
  • 1
    @Shoshi please note that the code "works" (just like the code in my sample using `block()`, but my problem isn't resolved, since the framework is still not working as documented or expected. Krishna's answer is an elegant workaround, but may have performance side-effects (it's not clear if this is the case) and it still doesn't make the template/model resolver work natively with reactive objects. It would also be very awkward if there were multiple futures that needed to all be collected and resolved before the template rendered. – JoshRivers May 01 '20 at 20:56
1

I was able to resolve this issue by removing the following from my pom.xml

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>

This was added without complaint by the Spring starter, but it is quasi-incompatible with Webflux. The application runs without complaint, but you can kinda tell something is going wrong when it reports in the startup logs that it is starting with Tomcat, not Netty. This is an indication it is running as an old-style MVC app, not a Webflux app. Having discovered this, I was able to find an explanatory answer on another question: https://stackoverflow.com/a/48418409/23276 (different symptoms, but same answer). There's a little more explanation here: https://stackoverflow.com/a/51378560/23276

I was able to prove things worked initially by creating a single-dependency sample app that worked the way I expected and saw in the documentation. Then I tried removing the different dependencies one by one until I found the conflict.

When troubleshooting this, I was impeded by my IDE doing weird things caching dependencies. It became easier to discover the problem when I dropped to the command line and tried things with mvn clean and mvn spring-boot:run until I found the broken dependency.

JoshRivers
  • 9,920
  • 8
  • 39
  • 39