diff --git a/src/main/java/run/halo/app/infra/exception/handlers/HaloErrorWebExceptionHandler.java b/src/main/java/run/halo/app/infra/exception/handlers/HaloErrorWebExceptionHandler.java index 4a7352d54..4331c1c07 100644 --- a/src/main/java/run/halo/app/infra/exception/handlers/HaloErrorWebExceptionHandler.java +++ b/src/main/java/run/halo/app/infra/exception/handlers/HaloErrorWebExceptionHandler.java @@ -12,6 +12,7 @@ import org.springframework.http.ProblemDetail; import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; import reactor.core.publisher.Mono; +import reactor.util.context.Context; import run.halo.app.theme.ThemeContext; import run.halo.app.theme.ThemeResolver; import run.halo.app.theme.engine.ThemeTemplateAvailabilityProvider; @@ -59,9 +60,9 @@ public class HaloErrorWebExceptionHandler extends DefaultErrorWebExceptionHandle @Override protected Mono renderErrorView(ServerRequest request) { - return themeResolver.getTheme(request.exchange().getRequest()) + return themeResolver.getTheme(request.exchange()) .flatMap(themeContext -> super.renderErrorView(request) - .contextWrite(context -> context.put(ThemeContext.class, themeContext))); + .contextWrite(Context.of(ThemeContext.class, themeContext))); } @Override diff --git a/src/main/java/run/halo/app/theme/HaloViewResolver.java b/src/main/java/run/halo/app/theme/HaloViewResolver.java index 5059591a2..46602bd7e 100644 --- a/src/main/java/run/halo/app/theme/HaloViewResolver.java +++ b/src/main/java/run/halo/app/theme/HaloViewResolver.java @@ -50,7 +50,7 @@ public class HaloViewResolver extends ThymeleafReactiveViewResolver { @Override public Mono render(Map model, MediaType contentType, ServerWebExchange exchange) { - return themeResolver.getTheme(exchange.getRequest()).flatMap(theme -> { + return themeResolver.getTheme(exchange).flatMap(theme -> { // calculate the engine before rendering setTemplateEngine(engineManager.getTemplateEngine(theme)); return super.render(model, contentType, exchange) diff --git a/src/main/java/run/halo/app/theme/ThemeContextBasedVariablesAcquirer.java b/src/main/java/run/halo/app/theme/ThemeContextBasedVariablesAcquirer.java index cb889ad19..346f41041 100644 --- a/src/main/java/run/halo/app/theme/ThemeContextBasedVariablesAcquirer.java +++ b/src/main/java/run/halo/app/theme/ThemeContextBasedVariablesAcquirer.java @@ -25,7 +25,7 @@ public class ThemeContextBasedVariablesAcquirer implements ViewContextBasedVaria @Override public Mono> acquire(ServerWebExchange exchange) { - return themeResolver.getTheme(exchange.getRequest()) + return themeResolver.getTheme(exchange) .flatMap(themeContext -> { String name = themeContext.getName(); return themeFinder.getByName(name); diff --git a/src/main/java/run/halo/app/theme/ThemeResolver.java b/src/main/java/run/halo/app/theme/ThemeResolver.java index 3503646b8..dd9b6c4c3 100644 --- a/src/main/java/run/halo/app/theme/ThemeResolver.java +++ b/src/main/java/run/halo/app/theme/ThemeResolver.java @@ -2,9 +2,9 @@ package run.halo.app.theme; import lombok.AllArgsConstructor; import org.apache.commons.lang3.StringUtils; -import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.util.Assert; +import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.SystemSetting.Theme; @@ -37,24 +37,37 @@ public class ThemeResolver { .map(ThemeContext.ThemeContextBuilder::build); } - public Mono getTheme(ServerHttpRequest request) { - return environmentFetcher.fetch(Theme.GROUP, Theme.class) - .map(Theme::getActive) - .switchIfEmpty(Mono.error(() -> new IllegalArgumentException("No theme activated"))) - .map(activatedTheme -> { - var builder = ThemeContext.builder(); - var themeName = - request.getQueryParams().getFirst(ThemeContext.THEME_PREVIEW_PARAM_NAME); - if (StringUtils.isBlank(themeName)) { - themeName = activatedTheme; - } - boolean active = StringUtils.equals(activatedTheme, themeName); - var path = themeRoot.get().resolve(themeName); - return builder.name(themeName) - .path(path) - .active(active) - .build(); - }); + public Mono getTheme(ServerWebExchange exchange) { + return fetchThemeFromExchange(exchange) + .switchIfEmpty(Mono.defer(() -> environmentFetcher.fetch(Theme.GROUP, Theme.class) + .map(Theme::getActive) + .switchIfEmpty( + Mono.error(() -> new IllegalArgumentException("No theme activated"))) + .map(activatedTheme -> { + var builder = ThemeContext.builder(); + var themeName = exchange.getRequest().getQueryParams() + .getFirst(ThemeContext.THEME_PREVIEW_PARAM_NAME); + if (StringUtils.isBlank(themeName)) { + themeName = activatedTheme; + } + boolean active = StringUtils.equals(activatedTheme, themeName); + var path = themeRoot.get().resolve(themeName); + return builder.name(themeName) + .path(path) + .active(active) + .build(); + }) + .doOnNext(themeContext -> + exchange.getAttributes().put(ThemeContext.class.getName(), themeContext)) + )); + } + + public Mono fetchThemeFromExchange(ServerWebExchange exchange) { + return Mono.justOrEmpty(exchange) + .map(ServerWebExchange::getAttributes) + .filter(attrs -> attrs.containsKey(ThemeContext.class.getName())) + .map(attrs -> attrs.get(ThemeContext.class.getName())) + .cast(ThemeContext.class); } } diff --git a/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java b/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java index 48a445fb4..c80d6a0cc 100644 --- a/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java +++ b/src/test/java/run/halo/app/theme/message/ThemeMessageResolverIntegrationTest.java @@ -1,12 +1,14 @@ package run.halo.app.theme.message; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + import java.io.FileNotFoundException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; import org.springframework.boot.test.context.SpringBootTest; @@ -14,13 +16,13 @@ import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.annotation.Bean; import org.springframework.http.MediaType; -import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.ResourceUtils; import org.springframework.web.reactive.function.server.RequestPredicates; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import run.halo.app.theme.ThemeContext; import run.halo.app.theme.ThemeResolver; @@ -50,7 +52,7 @@ public class ThemeMessageResolverIntegrationTest { defaultThemeUrl = ResourceUtils.getURL("classpath:themes/default"); otherThemeUrl = ResourceUtils.getURL("classpath:themes/other"); - Mockito.when(themeResolver.getTheme(Mockito.any(ServerHttpRequest.class))) + when(themeResolver.getTheme(any(ServerWebExchange.class))) .thenReturn(Mono.just(createDefaultContext())); } @@ -150,7 +152,7 @@ public class ThemeMessageResolverIntegrationTest { """); // For other theme - Mockito.when(themeResolver.getTheme(Mockito.any(ServerHttpRequest.class))) + when(themeResolver.getTheme(any(ServerWebExchange.class))) .thenReturn(Mono.just(createOtherContext())); webTestClient.get() .uri("/index?language=zh")