springboot 2.6.+中文资源名称无法访问,英文正常

原因:

从2.6.0开始Spring MVC 处理程序映射匹配请求路径的默认策略已从 AntPathMatcher 更改为PathPatternParser。
基本可以确定是这个更改导致的,不知道是不是bug,更改之后具体的不知道改动了哪些,能力有限,暂时未知
springboot一般配置资源是这样:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/files/**").addResourceLocations("file:E:/FileUpload/HmiInterface/";
    }
}

一点分析:

从addResourceHandlers追踪,最终发现会到ResourceHttpRequestHandler,通过ResourceHttpRequestHandler调试发现,在使用PathPatternParser 后,现在传进来的url是原始的未decode过的url,但是UrlPathHelper 默认设置是decodeURL的,这就导致重复进行了一次encode

----------ResourceHttpRequestHandler.java-------------
    @Nullable
    protected Resource getResource(HttpServletRequest request) throws IOException {
        //这里获取的path是原始的encode的URL,而小于2.6版本会根据UrlPathHelper 里设置的decodeurl会有不同的值
        String path = (String)request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        if (path == null) {
            throw new IllegalStateException("Required request attribute '" + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + "' is not set");
        } else {
            path = this.processPath(path);
            if (StringUtils.hasText(path) && !this.isInvalidPath(path)) {
                if (this.isInvalidEncodedPath(path)) {
                    return null;
                } else {
                    Assert.notNull(this.resolverChain, "ResourceResolverChain not initialized.");
                    Assert.notNull(this.transformerChain, "ResourceTransformerChain not initialized.");
                    //这里调用到PathResourceResolver
                    Resource resource = this.resolverChain.resolveResource(request, path, this.getLocations());
                    if (resource != null) {
                        resource = this.transformerChain.transform(request, resource);
                    }

                    return resource;
                }
            } else {
                return null;
            }
        }
    }
-----------PathResourceResolver.java-----------------

private String encodeOrDecodeIfNecessary(String path, @Nullable HttpServletRequest request, Resource location) {
        if (this.shouldDecodeRelativePath(location, request)) {
            return UriUtils.decode(path, StandardCharsets.UTF_8);
        } else if (this.shouldEncodeRelativePath(location) && request != null) {//因为UrlPathHelper 默认是decodeURL的,理论上这里的path应该是decode过的,所以这里会进行encode一次,便于查找文件时decode回来,但是现在这里的path都是encode过的,并不是原始的url,不论你UrlPathHelper 设置decode还是关闭,这就导致后面查文件的时候decode回来的是encode的url,所以找不到文件
            Charset charset = (Charset)this.locationCharsets.getOrDefault(location, StandardCharsets.UTF_8);
            StringBuilder sb = new StringBuilder();
            StringTokenizer tokenizer = new StringTokenizer(path, "/");

            while(tokenizer.hasMoreTokens()) {
                String value = UriUtils.encode(tokenizer.nextToken(), charset);
                sb.append(value);
                sb.append('/');
            }

            if (!path.endsWith("/")) {
                sb.setLength(sb.length() - 1);
            }

            return sb.toString();
        } else {
            return path;
        }
    }
private boolean shouldEncodeRelativePath(Resource location) {
        return location instanceof UrlResource && this.urlPathHelper != null && this.urlPathHelper.isUrlDecode();
    }

这就导致在AbstractFileResolvingResource.java getFile的时候获取不到文件了

public File getFile() throws IOException {
        URL url = this.getURL();
        return url.getProtocol().startsWith("vfs") ? AbstractFileResolvingResource.VfsResourceDelegate.getResource(url).getFile() : ResourceUtils.getFile(url, this.getDescription());
    }

因为这里ResourceUtils去获取文件时解码出来的是我们请求的原始的encodeurl,还需要在decode一次才是真正的文件名,所以我们可以关闭decode,但是这样会影响到哪些地方 未知

解决办法:

1.UrlPathHelper 设置不decodeurl
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper=new UrlPathHelper();
        urlPathHelper.setUrlDecode(false);
        urlPathHelper.setDefaultEncoding(StandardCharsets.UTF_8.name());
        configurer.setUrlPathHelper(urlPathHelper);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {  registry.addResourceHandler("/files/**").addResourceLocations("file:E:/FileUpload/HmiInterface/");
    }
}
2.使用原来的AntPathMatcher (推荐)
spring.mvc.pathmatch.matching-strategy=ant-path-matcher

能力有限,往上不是很好去调试了,不知道这算不算是bug吧,等待后续观察。

Spring Boot 2.6.x中,可以使用Spring Data Elasticsearch来整合Elasticsearch。Spring Data Elasticsearch提供了一组用于构建Elasticsearch应用程序的API,并且可以轻松地与Spring Boot集成。下面是整合步骤: 1. 添加Spring Data Elasticsearch依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> ``` 2. 配置Elasticsearch连接信息: 在application.properties中添加以下配置: ``` spring.data.elasticsearch.cluster-name=elasticsearch spring.data.elasticsearch.cluster-nodes=localhost:9300 ``` 其中,cluster-name为Elasticsearch集群名称,cluster-nodes为Elasticsearch节点地址。 3. 创建Elasticsearch实体类: ``` @Document(indexName = "user", type = "_doc") public class User { @Id private String id; private String name; private int age; // getters and setters } ``` 其中,@Document注解用于指定索引名称和类型,@Id注解用于指定文档ID。 4. 创建Elasticsearch仓库类: ``` @Repository public interface UserRepository extends ElasticsearchRepository<User, String> { } ``` 其中,UserRepository继承自ElasticsearchRepository,可以直接使用Spring Data Elasticsearch提供的方法进行数据操作。 5. 在Service中使用Elasticsearch仓库类: ``` @Service public class UserService { @Autowired private UserRepository userRepository; public void save(User user) { userRepository.save(user); } public User findById(String id) { return userRepository.findById(id).orElse(null); } public void deleteById(String id) { userRepository.deleteById(id); } public List<User> findAll() { return (List<User>) userRepository.findAll(); } } ``` 其中,save、findById、deleteById、findAll方法都是由ElasticsearchRepository提供的。 至此,Spring Boot 2.6.x整合Elasticsearch的步骤就完成了。通过使用Spring Data Elasticsearch,我们可以轻松地进行CRUD操作。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值