mirror of https://github.com/halo-dev/halo
Overwrite Jackson2Json decoder and encoder (#2145)
parent
02a7143fe5
commit
a956758a2f
|
@ -1,10 +1,14 @@
|
|||
package run.halo.app.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.util.List;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.codec.CodecConfigurer;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
|
@ -16,6 +20,12 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
|||
@EnableWebFlux
|
||||
public class WebFluxConfig implements WebFluxConfigurer {
|
||||
|
||||
final ObjectMapper objectMapper;
|
||||
|
||||
public WebFluxConfig(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerResponse.Context context(CodecConfigurer codec,
|
||||
ViewResolutionResultHandler resultHandler) {
|
||||
|
@ -33,4 +43,13 @@ public class WebFluxConfig implements WebFluxConfigurer {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
|
||||
// we need to customize the Jackson2Json[Decoder][Encoder] here to serialize and
|
||||
// deserialize special types, e.g.: Instant, LocalDateTime. So we use ObjectMapper
|
||||
// created by outside.
|
||||
configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper));
|
||||
configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,10 +73,14 @@ public class WebServerSecurityConfig {
|
|||
}
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
SecurityWebFilterChain webFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange(
|
||||
exchanges -> exchanges.pathMatchers("/v3/api-docs/**", "/v3/api-docs.yaml",
|
||||
"/swagger-ui/**", "/swagger-ui.html", "/webjars/**").permitAll())
|
||||
http.authorizeExchange(exchanges -> exchanges.pathMatchers(
|
||||
"/v3/api-docs/**",
|
||||
"/v3/api-docs.yaml",
|
||||
"/swagger-ui/**",
|
||||
"/swagger-ui.html",
|
||||
"/webjars/**").permitAll())
|
||||
.authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
|
||||
.cors(withDefaults())
|
||||
.httpBasic(withDefaults())
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
server:
|
||||
port: 8090
|
||||
spring:
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
server:
|
||||
port: 8090
|
||||
spring:
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
enabled: detect
|
||||
datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
package run.halo.app.config;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
|
||||
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
|
||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureWebTestClient
|
||||
@Import(ServerCodecTest.TestConfig.class)
|
||||
class ServerCodecTest {
|
||||
|
||||
static final String INSTANT = "2022-06-09T10:57:30Z";
|
||||
|
||||
static final String LOCAL_DATE_TIME = "2022-06-10T10:57:30";
|
||||
|
||||
@Autowired
|
||||
WebTestClient webClient;
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void timeSerializationTest() {
|
||||
webClient.get().uri("/fake/api/times")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||||
.expectBody()
|
||||
.jsonPath("$.instant").value(equalTo(INSTANT))
|
||||
.jsonPath("$.localDateTime").value(equalTo(LOCAL_DATE_TIME))
|
||||
;
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void timeDeserializationTest() {
|
||||
webClient
|
||||
.mutateWith(csrf())
|
||||
.post().uri("/fake/api/time/report")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(Map.of("now", Instant.parse(INSTANT)))
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectHeader().contentType(MediaType.APPLICATION_JSON)
|
||||
.expectBody(new ParameterizedTypeReference<Map<String, Instant>>() {
|
||||
}).isEqualTo(Map.of("now", Instant.parse(INSTANT)))
|
||||
;
|
||||
}
|
||||
|
||||
@TestConfiguration(proxyBeanMethods = false)
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
RouterFunction<ServerResponse> timesRouter() {
|
||||
return route().GET("/fake/api/times", request -> {
|
||||
var times = Map.of("instant", Instant.parse(INSTANT),
|
||||
"localDateTime", LocalDateTime.parse(LOCAL_DATE_TIME));
|
||||
return ServerResponse
|
||||
.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(times);
|
||||
}).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RouterFunction<ServerResponse> reportTime() {
|
||||
final var type = new ParameterizedTypeReference<Map<String, Instant>>() {
|
||||
};
|
||||
return route().POST("/fake/api/time/report",
|
||||
contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)),
|
||||
request -> ServerResponse.ok()
|
||||
.body(request.bodyToMono(type), type))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
server:
|
||||
port: 8090
|
||||
spring:
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
output:
|
||||
ansi:
|
||||
enabled: detect
|
||||
|
|
Loading…
Reference in New Issue