开发笔记

2021-12-29 Views Java | Spring Boot585字4 min read

1. Jackson 配置不生效

程序中使用@EnableWebMvc时配置文件中有关Jackson的配置不会生效

As described in the documentation, when you use @EnableWebMvc you are indicating that you will take complete control over Spring MVC's configuration. That includes configuring how it uses Jackson to render responses
https://github.com/spring-projects/spring-boot/issues/9941

2. 下载文件中文名乱码以及指定文件编码类型以及文件大小

response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(filename, "utf8"));
response.setContentLength(decode.length);

3. gateway 踩坑

1. 使用 feign 时需要自己装载 HttpMessageConverters

@Bean
    public HttpMessageConverters httpMessageConverters(MyMappingJackson2HttpMessageConverter converter) {
        return new HttpMessageConverters(converter);
    }


    @Bean
    public Decoder feignDecoder(MyMappingJackson2HttpMessageConverter converter) {
        ObjectFactory<HttpMessageConverters> factory = () -> new HttpMessageConverters(converter);
        return new OptionalDecoder(
                new ResponseEntityDecoder(new SpringDecoder(factory)));
    }

    @Bean
    public MyMappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        return new MyMappingJackson2HttpMessageConverter();
    }

    public static class MyMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
        public MyMappingJackson2HttpMessageConverter() {
            List<MediaType> mediaTypeList = new ArrayList<>(this.getSupportedMediaTypes());
            mediaTypeList.add(MediaType.TEXT_PLAIN);
            this.setSupportedMediaTypes(mediaTypeList);
        }

    }

2. gateway服务不能包含 tomcat 的依赖

3. filter 中调用 feign 会报错

java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-N

原因是gateway 的 filter 是基于 webFlux 响应式的,而 feign 是同步的, 所以要把调用改成异步调用也就是 Future, 例如:

    ExecutorService executorService = Executors.newCachedThreadPool();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (vionConfig.getAuthEnabled() && !skip(request)) {
            String token = getToken(request);
            if (token == null) {
                return unAuthorized(exchange.getResponse(), "authorization failed");
            }

            // 鉴权
            ImmutablePair<Boolean, String> checkResult = checkToken(token);
            Boolean success = checkResult.left;
            if (!success) {
                log.info("token校验未通过:[{}][{}]", checkResult.right, request.getPath());
                return unAuthorized(exchange.getResponse(), checkResult.right);
            }
            // 添加 username 到 header 中, 供之后的服务使用
            ServerHttpRequest req = request.mutate().headers(headers -> headers.add("username", checkResult.right)).build();
            exchange.mutate().request(req).build();

            log.info("token 校验成功:{}", token);
        }
        return chain.filter(exchange);
    }
    
    private ImmutablePair<Boolean, String> checkToken(String token) {
        // todo authorize
        String username;
        try {
            Map<String, String> api = new HashMap<>(2);
            api.put("verb", HttpMethod.POST.name());
            api.put("path", "/api/v1/auth/authorize");
            Map<String, Object> perm = new HashMap<>(2);
            perm.put("type", "api");
            perm.put("api", api);
            // webFlux 是异步的,这里不能直接用 feign 调用
            Future<JSONObject> perm1 = executorService.submit(() -> authClient.checkToken(token, Collections.singletonMap("perm", perm)));
            JSONObject jsonObject = perm1.get();
            username = jsonObject.getString("username");
        } catch (Exception e) {
            log.info("token 校验失败:", e);
            return ImmutablePair.of(false, e.getLocalizedMessage());
        }
        return ImmutablePair.of(true, username);
    }

也可以使用 CompletableFuture.supplyAsync() 方法

4. Redisson LocalCachedMap

对于同一个缓存对象,LocalCachedMap创建一次即可,多次创建会导致内存泄漏

LocalCachedMap 创建一次即可

EOF