mirror of https://github.com/halo-dev/halo
Fix the problem of not being able to request SinglePage with slug name containing special characters (#2479)
#### What type of PR is this? /kind bug /kind api-change /area core /milestone 2.0 #### What this PR does / why we need it: Support setting special characters on slug name of SinglePage. #### Which issue(s) this PR fixes: Partial Fixes https://github.com/halo-dev/halo/issues/2473 #### Special notes for your reviewer: Steps to test: 1. Create single pags with slug name `中文`, `/a/b/c/d` or `a / b` 2. Request it with permalink generated by Halo #### Does this PR introduce a user-facing change? ```release-note None ```pull/2493/head
parent
e954af88ec
commit
565c8340e9
|
@ -1,5 +1,8 @@
|
|||
package run.halo.app.core.extension.reconciler;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.springframework.web.util.UriUtils.encodePath;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -150,8 +153,10 @@ public class SinglePageReconciler implements Reconciler<Reconciler.Request> {
|
|||
final SinglePage oldPage = JsonUtils.deepCopy(singlePage);
|
||||
permalinkOnDelete(oldPage);
|
||||
|
||||
var permalink = encodePath(singlePage.getSpec().getSlug(), UTF_8);
|
||||
permalink = StringUtils.prependIfMissing(permalink, "/");
|
||||
singlePage.getStatusOrDefault()
|
||||
.setPermalink(PathUtils.combinePath(singlePage.getSpec().getSlug()));
|
||||
.setPermalink(permalink);
|
||||
if (isPublished(singlePage)) {
|
||||
permalinkOnAdd(singlePage);
|
||||
}
|
||||
|
|
|
@ -200,7 +200,6 @@ public class SpringWebFluxTemplateEngine extends SpringTemplateEngine
|
|||
try {
|
||||
|
||||
process(templateName, markupSelectors, context, writer);
|
||||
Mono.empty().block();
|
||||
|
||||
} catch (final Throwable t) {
|
||||
logger.error(
|
||||
|
|
|
@ -157,6 +157,24 @@ public class PermalinkIndexer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension name by permalink.
|
||||
*
|
||||
* @param gvk is GroupVersionKind of extension
|
||||
* @param permalink is encoded permalink
|
||||
* @return extension name or null
|
||||
*/
|
||||
@Nullable
|
||||
public String getNameByPermalink(GroupVersionKind gvk, String permalink) {
|
||||
readWriteLock.readLock().lock();
|
||||
try {
|
||||
var locator = permalinkLocatorLookup.get(permalink);
|
||||
return locator == null ? null : locator.name();
|
||||
} finally {
|
||||
readWriteLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for test.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package run.halo.app.theme.router.strategy;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.method;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicate;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
public enum PermalinkPredicates {
|
||||
;
|
||||
|
||||
public static RequestPredicate get(String permalink) {
|
||||
return method(HttpMethod.GET).and(path(UriUtils.decode(permalink, UTF_8)));
|
||||
}
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
package run.halo.app.theme.router.strategy;
|
||||
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||
import static run.halo.app.theme.router.strategy.PermalinkPredicates.get;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicate;
|
||||
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;
|
||||
|
@ -49,13 +48,12 @@ public class SinglePageRouteStrategy implements TemplateRouterStrategy {
|
|||
|
||||
List<String> permalinks = permalinkIndexer.getPermalinks(gvk);
|
||||
for (String permalink : permalinks) {
|
||||
requestPredicate = requestPredicate.or(RequestPredicates.GET(permalink));
|
||||
requestPredicate = requestPredicate.or(get(permalink));
|
||||
}
|
||||
|
||||
return RouterFunctions
|
||||
.route(requestPredicate.and(accept(MediaType.TEXT_HTML)), request -> {
|
||||
String slug = StringUtils.removeStart(request.path(), "/");
|
||||
String name = permalinkIndexer.getNameBySlug(gvk, slug);
|
||||
var name = permalinkIndexer.getNameByPermalink(gvk, request.path());
|
||||
if (name == null) {
|
||||
return ServerResponse.notFound().build();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package run.halo.app.theme.router;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -106,4 +108,16 @@ class PermalinkIndexerTest {
|
|||
permalinkIndexer.getNameBySlug(gvk, "nothing");
|
||||
}).isInstanceOf(NoSuchElementException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNameByPermalink() {
|
||||
ExtensionLocator locator = new ExtensionLocator(gvk, "test-name", "test-slug");
|
||||
permalinkIndexer.register(locator, "/test-permalink");
|
||||
|
||||
var name = permalinkIndexer.getNameByPermalink(gvk, "/test-permalink");
|
||||
assertEquals("test-name", name);
|
||||
|
||||
name = permalinkIndexer.getNameByPermalink(gvk, "/invalid-permalink");
|
||||
assertNull(name);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,9 @@ import static org.mockito.ArgumentMatchers.any;
|
|||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static run.halo.app.theme.DefaultTemplateEnum.SINGLE_PAGE;
|
||||
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -13,15 +15,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.extension.ListResult;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.finders.SinglePageFinder;
|
||||
import run.halo.app.theme.router.PermalinkIndexer;
|
||||
|
||||
|
@ -50,37 +48,66 @@ class SinglePageRouteStrategyTest {
|
|||
void setUp() {
|
||||
lenient().when(singlePageFinder.list(anyInt(), anyInt()))
|
||||
.thenReturn(new ListResult<>(1, 10, 0, List.of()));
|
||||
when(permalinkIndexer.getPermalinks(any()))
|
||||
.thenReturn(List.of("/fake-slug"));
|
||||
when(permalinkIndexer.getNameBySlug(any(), eq("fake-slug")))
|
||||
.thenReturn("fake-name");
|
||||
lenient().when(viewResolver.resolveViewName(eq(SINGLE_PAGE.getValue()), any()))
|
||||
.thenReturn(Mono.just(new EmptyView()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRouteFunction() {
|
||||
RouterFunction<ServerResponse> routeFunction =
|
||||
strategy.getRouteFunction(DefaultTemplateEnum.SINGLE_PAGE.getValue(),
|
||||
null);
|
||||
void shouldResponse404IfNoPermalinkFound() {
|
||||
createClient().get()
|
||||
.uri("/nothing")
|
||||
.exchange()
|
||||
.expectStatus().isNotFound();
|
||||
}
|
||||
|
||||
WebTestClient client = WebTestClient.bindToRouterFunction(routeFunction)
|
||||
.handlerStrategies(HandlerStrategies.builder()
|
||||
.viewResolver(viewResolver)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
when(viewResolver.resolveViewName(eq(DefaultTemplateEnum.SINGLE_PAGE.getValue()), any()))
|
||||
.thenReturn(Mono.just(new EmptyView()));
|
||||
|
||||
client.get()
|
||||
@Test
|
||||
void shouldResponse200IfPermalinkFound() {
|
||||
when(permalinkIndexer.getPermalinks(any()))
|
||||
.thenReturn(List.of("/fake-slug"));
|
||||
when(permalinkIndexer.getNameByPermalink(any(), eq("/fake-slug")))
|
||||
.thenReturn("fake-name");
|
||||
createClient().get()
|
||||
.uri("/fake-slug")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isOk();
|
||||
|
||||
client.get()
|
||||
.uri("/nothing")
|
||||
.exchange()
|
||||
.expectStatus()
|
||||
.isEqualTo(HttpStatus.NOT_FOUND);
|
||||
verify(permalinkIndexer).getNameByPermalink(any(), eq("/fake-slug"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResponse200IfSlugNameContainsSpecialChars() {
|
||||
when(permalinkIndexer.getPermalinks(any()))
|
||||
.thenReturn(List.of("/fake%20/%20slug"));
|
||||
when(permalinkIndexer.getNameByPermalink(any(), eq("/fake%20/%20slug")))
|
||||
.thenReturn("fake-name");
|
||||
createClient().get()
|
||||
.uri("/fake / slug")
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
verify(permalinkIndexer).getNameByPermalink(any(), eq("/fake%20/%20slug"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResponse200IfSlugNameContainsChineseChars() {
|
||||
when(permalinkIndexer.getPermalinks(any()))
|
||||
.thenReturn(List.of("/%E4%B8%AD%E6%96%87"));
|
||||
when(permalinkIndexer.getNameByPermalink(any(), eq("/%E4%B8%AD%E6%96%87")))
|
||||
.thenReturn("fake-name");
|
||||
createClient().get()
|
||||
.uri("/中文")
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
verify(permalinkIndexer).getNameByPermalink(any(), eq("/%E4%B8%AD%E6%96%87"));
|
||||
}
|
||||
|
||||
WebTestClient createClient() {
|
||||
var routeFunction = strategy.getRouteFunction(SINGLE_PAGE.getValue(), null);
|
||||
|
||||
return WebTestClient.bindToRouterFunction(routeFunction)
|
||||
.handlerStrategies(HandlerStrategies.builder()
|
||||
.viewResolver(viewResolver)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue