spring icon indicating copy to clipboard operation
spring copied to clipboard

vaadinRootMapping conflicts with resourceHandlerMapping

Open Frettman opened this issue 4 years ago • 10 comments

UPDATE Look at https://github.com/vaadin/spring/issues/602#issuecomment-613880654 for resolution

I'm using Vaadin in a Spring Boot application. The vaadinRootMapping bean in VaadinServletConfiguration has the same order (Ordered.LOWEST_PRECEDENCE - 1) as MVC's resourceHandlerMapping bean in WebMvcConfigurationSupport. Technically this means it's undefined which one is considered first, but for me the vaadinRootMapping has consistently had the higher effective precedence. This means that URLs matching /webjars/** or //swagger-ui/ were directed to the vaadinRootMapping instead of the resourceHandlerMapping, leading to a 404 for certain things. With a WebMvcConfigurer it's possible to change the order value of the resourceHandlerMapping:

@Component
public class ResourceHandlerMappingOrderFix
  implements WebMvcConfigurer
{

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry)
  {
    registry.setOrder(Ordered.LOWEST_PRECEDENCE - 2);
  }

}

But unfortunately, just like the vaadinRootMapping, the resourceHandlerMapping also has a pattern of /**. This means either Vaadin works or anything registered using the ResourceHandlerRegistry works, but not both. So changing the order of resourceHandlerMapping ultimately only makes it worse.

~~I've experimented with setting vaadin.url-mapping to something other than /* but I never could make that work even on the most basic level. Yes, i tried registering the additional VaadinServlet mentioned in the in the documentation. (And if it worked, shouldn't this be done by the configuration automatically?)~~ After using vaadin.urlMapping (instead of vaadin.url-mapping) mapping Vaadin to /ui/* seems to work. That's a solution I can easily live with. But: Spring Boot will accept both vaadin.url-mapping and vaadin.urlMapping when mapping to VaadinConfigurationProperties, with vaadin.url-mapping even being the canonical spelling. That's why IDEs keep suggesting it. So I strongly encourage you to also support both variations in RootMappedCondition. Otherwise people will use vaadin.url-mapping which will superficially change the URL mapping, but the pages won't load.

So, to sum it up:

  1. That vaadinRootMapping and resourceHandlerMapping share the same order value is problematic in any case, because that means it's basically coincidence which one gets picked first. Move the vaadinRootMapping to Ordered.LOWEST_PRECEDENCE - 2 for example.
  2. ~~Is there a way to either restrict the VaadinServlet to a certain path (like /ui) or any other way to allow coexistence with MVC's resource handlers?~~ Also support vaadin.url-mapping in RootMappedCondition.
  3. Extend the Spring (Boot) documentation to say that vaadin.urlMapping is necessary to make MVC's resource handlers work. Or find a way to just make it work :) Those should be fairly simple changes that could save some poor soul a nasty debug session :)

Frettman avatar Apr 10 '20 11:04 Frettman

Will Ordered.LOWEST_PRECEDENCE - 2 actually work ?

It less than Ordered.LOWEST_PRECEDENCE - 1 which means with Ordered.LOWEST_PRECEDENCE - 2 it will take precedence over Ordered.LOWEST_PRECEDENCE - 1 . But you need the opposite: resourceHandlerMapping should take precedence over Vaadin which means resourceHandlerMapping should have order number less than Ordered.LOWEST_PRECEDENCE - 1. So in fact vaadinRootMapping should be Ordered.LOWEST_PRECEDENCE but I'm not sure whether it's feasible: won't it break something else ?

denis-anisimov avatar Apr 14 '20 11:04 denis-anisimov

Sorry, if I wasn't clear enough. Both vaadinRootMapping and resourceHandlerMapping have a mapping for /*, so no matter which one takes precedence, something will be broken. Setting vaadinRootMapping to Ordered.LOWEST_PRECEDENCE - 2 at least would make the status quo (vaadinRootMapping taking precedence) deliberate, instead of just by coincidence.

Things being as they are, Vaadin will not work if the resourceHandlerMapping is queried first. So my reasoning was something like this: If I include vaadin-spring-boot-starter in my project, I want Vaadin to work -> vaadinRootMapping should have higher precedence. If I also want resourceHandlerMapping to work, I have to additionally set e.g. vaadin.urlMapping = /ui/* That way both mappings won't conflict anymore.

From the point of view of my application this seems perfectly fine; I wanted that /ui prefix for Vaadin anyway. But of course it would be nice if things just worked out of the box.

One option worth exploring could be using a custom implementation of MatchableHandlerMapping instead of the purely URL-pattern-based SimpleUrlHandlerMapping. The basic idea is to only match requests that can actually be handled by Vaadin. E.g. because there's a route registered for the URL or because it's a framework request (ApplicationConstants.REQUEST_TYPE_PARAMETER). Everything else would pass through to the resourceHandlerMapping.

Frettman avatar Apr 14 '20 14:04 Frettman

OK, so

  • vaadinRootMapping should have more priority than resourceHandlerMapping.

To achieve this we should use Ordered.LOWEST_PRECEDENCE - 2 for Vaadin mapping. I think that's OK.

Another request :

  • Support vaadin.url-mapping in addition to vaadin.urlMapping property. That's OK.

  • Extend the Spring (Boot) documentation to say that vaadin.urlMapping is necessary to make MVC's resource handlers work.

OK. But that's for another repo. I will make a ticket about this.

E.g. because there's a route registered for the URL or because it's a framework request

That won't work unfortunately because there is a way to manage routes dynamically: we may not know which routes application registers at the application start. Only some initial set of routes are known because of annotations. But they may be removed/added at any point.

Please make another ticket for this if you see another way to avoid collisions. It's worth anyway to track this separately because this ticket is about something else even though this is the original problem.

denis-anisimov avatar Apr 15 '20 07:04 denis-anisimov

Documentation ticket : https://github.com/vaadin/flow-and-components-documentation/issues/1210

denis-anisimov avatar Apr 15 '20 08:04 denis-anisimov

Thanks a lot :) I have a prototype for a HandlerMapping that queries session and application RouteRegistries, so the dynamic part should be covered. The only major issue are persistent sessions after being restored. Transient information hasn't been restored yet when I access them in the HandlerMapping. But I'll create a new ticket for that.

Frettman avatar Apr 15 '20 11:04 Frettman

I've stubled over the same issue when trying to enable Swagger-UI using springdoc where vaadin.urlMapping is on default. So I just try to raise attention for this...

chkpnt avatar Jan 18 '21 15:01 chkpnt

Hi, I have been not able to make it work without moving vaadin to another url map. Has anybody make it work?

estronque avatar Jun 10 '21 09:06 estronque

Related to https://github.com/vaadin/flow/issues/10163

TatuLund avatar Feb 07 '22 18:02 TatuLund

Looks like the issue is still around in Vaadin 22.0.4. Iam trying to get Swagger-UI using springdoc with vaadin.urlMapping on default. Is there a workaround or will there be a "fix"?

DerAzubi avatar Feb 08 '22 08:02 DerAzubi

Looks like the issue is still around in Vaadin 22.0.4. Iam trying to get Swagger-UI using springdoc with vaadin.urlMapping on default. Is there a workaround or will there be a "fix"?

Both Vaadin and Spring's resourceHandlerMapping (which would provide the Swagger UI) try to grab /*. So no matter their order, something will be broken. The only solution I see is teaching one of them to only claim requests that are really meant for them and let other requests pass through to the next handler(s). That's something I proposed and prototyped in #604.

Frettman avatar Feb 08 '22 09:02 Frettman

This was fixed in https://github.com/vaadin/flow/pull/14579 but not in a fully automatic way. You can define paths to exclude, e.g. vaadin.excludeUrls=/swagger-ui/** and those will not be handled by the vaadin servlet when it is mapped to /*

Artur- avatar Oct 03 '22 07:10 Artur-