mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-19 12:57:10 +08:00
refactor(web): 拆分 web 模块
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.annotation;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import top.continew.starter.web.autoconfigure.response.GlobalResponseAutoConfiguration;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 全局响应启用注解
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({GlobalResponseAutoConfiguration.class})
|
||||
public @interface EnableGlobalResponse {}
|
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.cors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
/**
|
||||
* 跨域自动配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Lazy
|
||||
@AutoConfiguration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.WEB_CORS, name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||
@EnableConfigurationProperties(CorsProperties.class)
|
||||
public class CorsAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CorsAutoConfiguration.class);
|
||||
|
||||
/**
|
||||
* 跨域过滤器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CorsFilter corsFilter(CorsProperties properties) {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
// 设置跨域允许时间
|
||||
config.setMaxAge(1800L);
|
||||
// 配置允许跨域的域名
|
||||
if (properties.getAllowedOrigins().contains(StringConstants.ASTERISK)) {
|
||||
config.addAllowedOriginPattern(StringConstants.ASTERISK);
|
||||
} else {
|
||||
// 配置为 true 后则必须配置允许跨域的域名,且不允许配置为 *
|
||||
config.setAllowCredentials(true);
|
||||
properties.getAllowedOrigins().forEach(config::addAllowedOrigin);
|
||||
}
|
||||
// 配置允许跨域的请求方式
|
||||
properties.getAllowedMethods().forEach(config::addAllowedMethod);
|
||||
// 配置允许跨域的请求头
|
||||
properties.getAllowedHeaders().forEach(config::addAllowedHeader);
|
||||
// 配置允许跨域的响应头
|
||||
properties.getExposedHeaders().forEach(config::addExposedHeader);
|
||||
// 添加映射路径,拦截一切请求
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration(StringConstants.PATH_PATTERN, config);
|
||||
CorsFilter corsFilter = new CorsFilter(source);
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Web-CorsFilter' completed initialization.");
|
||||
return corsFilter;
|
||||
}
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.cors;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 跨域配置属性
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties(PropertiesConstants.WEB_CORS)
|
||||
public class CorsProperties {
|
||||
|
||||
private static final List<String> ALL = Collections.singletonList(StringConstants.ASTERISK);
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 允许跨域的域名
|
||||
*/
|
||||
private List<String> allowedOrigins = new ArrayList<>(ALL);
|
||||
|
||||
/**
|
||||
* 允许跨域的请求方式
|
||||
*/
|
||||
private List<String> allowedMethods = new ArrayList<>(ALL);
|
||||
|
||||
/**
|
||||
* 允许跨域的请求头
|
||||
*/
|
||||
private List<String> allowedHeaders = new ArrayList<>(ALL);
|
||||
|
||||
/**
|
||||
* 允许跨域的响应头
|
||||
*/
|
||||
private List<String> exposedHeaders = new ArrayList<>();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public List<String> getAllowedOrigins() {
|
||||
return allowedOrigins;
|
||||
}
|
||||
|
||||
public void setAllowedOrigins(List<String> allowedOrigins) {
|
||||
this.allowedOrigins = allowedOrigins;
|
||||
}
|
||||
|
||||
public List<String> getAllowedMethods() {
|
||||
return allowedMethods;
|
||||
}
|
||||
|
||||
public void setAllowedMethods(List<String> allowedMethods) {
|
||||
this.allowedMethods = allowedMethods;
|
||||
}
|
||||
|
||||
public List<String> getAllowedHeaders() {
|
||||
return allowedHeaders;
|
||||
}
|
||||
|
||||
public void setAllowedHeaders(List<String> allowedHeaders) {
|
||||
this.allowedHeaders = allowedHeaders;
|
||||
}
|
||||
|
||||
public List<String> getExposedHeaders() {
|
||||
return exposedHeaders;
|
||||
}
|
||||
|
||||
public void setExposedHeaders(List<String> exposedHeaders) {
|
||||
this.exposedHeaders = exposedHeaders;
|
||||
}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.mvc;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import top.continew.starter.web.autoconfigure.mvc.converter.BaseEnumConverterFactory;
|
||||
import top.continew.starter.web.autoconfigure.mvc.converter.time.DateConverter;
|
||||
import top.continew.starter.web.autoconfigure.mvc.converter.time.LocalDateConverter;
|
||||
import top.continew.starter.web.autoconfigure.mvc.converter.time.LocalDateTimeConverter;
|
||||
import top.continew.starter.web.autoconfigure.mvc.converter.time.LocalTimeConverter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Web MVC 自动配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@EnableWebMvc
|
||||
@AutoConfiguration
|
||||
public class WebMvcAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WebMvcAutoConfiguration.class);
|
||||
private final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter;
|
||||
|
||||
public WebMvcAutoConfiguration(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
|
||||
this.mappingJackson2HttpMessageConverter = mappingJackson2HttpMessageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解决 Jackson2ObjectMapperBuilderCustomizer 配置不生效的问题
|
||||
* <p>
|
||||
* MappingJackson2HttpMessageConverter 对象在程序启动时创建了多个,移除多余的,保证只有一个
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
|
||||
if (Objects.isNull(mappingJackson2HttpMessageConverter)) {
|
||||
converters.add(0, new MappingJackson2HttpMessageConverter());
|
||||
} else {
|
||||
converters.add(0, mappingJackson2HttpMessageConverter);
|
||||
}
|
||||
// 自定义 converters 时,需要手动在最前面添加 ByteArrayHttpMessageConverter
|
||||
// 否则 Spring Doc OpenAPI 的 /*/api-docs/**(例如:/v3/api-docs/default)接口响应内容会变为 Base64 编码后的内容,最终导致接口文档解析失败
|
||||
// 详情请参阅:https://github.com/springdoc/springdoc-openapi/issues/2143
|
||||
converters.add(0, new ByteArrayHttpMessageConverter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
registry.addConverterFactory(new BaseEnumConverterFactory());
|
||||
registry.addConverter(new DateConverter());
|
||||
registry.addConverter(new LocalDateTimeConverter());
|
||||
registry.addConverter(new LocalDateConverter());
|
||||
registry.addConverter(new LocalTimeConverter());
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Web MVC' completed initialization.");
|
||||
}
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.mvc.converter;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import top.continew.starter.core.enums.BaseEnum;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* BaseEnum 参数转换器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public class BaseEnumConverter<T extends BaseEnum> implements Converter<String, T> {
|
||||
|
||||
private final Map<String, T> enumMap = new HashMap<>();
|
||||
|
||||
public BaseEnumConverter(Class<T> enumType) {
|
||||
T[] enums = enumType.getEnumConstants();
|
||||
for (T e : enums) {
|
||||
enumMap.put(String.valueOf(e.getValue()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convert(String source) {
|
||||
return enumMap.get(source);
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.mvc.converter;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConverterFactory;
|
||||
import top.continew.starter.core.enums.BaseEnum;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* BaseEnum 参数转换器工厂
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public class BaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {
|
||||
|
||||
private static final Map<Class, Converter> CONVERTER_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
|
||||
return CONVERTER_CACHE.computeIfAbsent(targetType, key -> new BaseEnumConverter<>(targetType));
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.mvc.converter.time;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Date 参数转换器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public class DateConverter implements Converter<String, Date> {
|
||||
|
||||
@Override
|
||||
public Date convert(String source) {
|
||||
return DateUtil.parse(source);
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.mvc.converter.time;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* LocalDate 参数转换器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public class LocalDateConverter implements Converter<String, LocalDate> {
|
||||
|
||||
@Override
|
||||
public LocalDate convert(String source) {
|
||||
return DateUtil.parse(source).toLocalDateTime().toLocalDate();
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.mvc.converter.time;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* LocalDateTime 参数转换器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
|
||||
|
||||
@Override
|
||||
public LocalDateTime convert(String source) {
|
||||
return DateUtil.parse(source).toLocalDateTime();
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.mvc.converter.time;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* LocalTime 参数转换器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public class LocalTimeConverter implements Converter<String, LocalTime> {
|
||||
|
||||
@Override
|
||||
public LocalTime convert(String source) {
|
||||
return DateUtil.parse(source).toLocalDateTime().toLocalTime();
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.response;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import org.apache.commons.lang3.reflect.TypeUtils;
|
||||
import org.springdoc.core.parsers.ReturnTypeParser;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import top.continew.starter.apidoc.util.DocUtils;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* SpringDoc 全局响应处理器
|
||||
* <p>
|
||||
* 接口文档全局添加响应格式 {@link com.feiniaojin.gracefulresponse.data.Response}
|
||||
* </p>
|
||||
*
|
||||
* @author echo
|
||||
* @since 2.5.2
|
||||
*/
|
||||
public class ApiDocGlobalResponseHandler implements ReturnTypeParser {
|
||||
|
||||
private final GlobalResponseProperties globalResponseProperties;
|
||||
private final Class<Object> responseClass;
|
||||
|
||||
public ApiDocGlobalResponseHandler(GlobalResponseProperties globalResponseProperties) {
|
||||
this.globalResponseProperties = globalResponseProperties;
|
||||
this.responseClass = ClassUtil.loadClass(globalResponseProperties.getResponseClassFullName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取返回类型
|
||||
*
|
||||
* @param methodParameter 方法参数
|
||||
* @return {@link Type }
|
||||
*/
|
||||
@Override
|
||||
public Type getReturnType(MethodParameter methodParameter) {
|
||||
// 获取返回类型
|
||||
Type returnType = ReturnTypeParser.super.getReturnType(methodParameter);
|
||||
// 判断是否具有 RestController 注解
|
||||
if (!DocUtils.hasRestControllerAnnotation(methodParameter.getContainingClass())) {
|
||||
return returnType;
|
||||
}
|
||||
// 如果为响应类型,则直接返回
|
||||
if (returnType.getTypeName().contains(globalResponseProperties.getResponseClassFullName())) {
|
||||
return returnType;
|
||||
}
|
||||
// 如果是 void类型,则返回 R<Void>
|
||||
if (returnType == void.class || returnType == Void.class) {
|
||||
return TypeUtils.parameterize(responseClass, Void.class);
|
||||
}
|
||||
// 返回 R<T>
|
||||
return TypeUtils.parameterize(responseClass, returnType);
|
||||
}
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.response;
|
||||
|
||||
import com.feiniaojin.gracefulresponse.advice.lifecycle.exception.BeforeControllerAdviceProcess;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 默认回调处理器实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public class DefaultBeforeControllerAdviceProcessImpl implements BeforeControllerAdviceProcess {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(DefaultBeforeControllerAdviceProcessImpl.class);
|
||||
private final GlobalResponseProperties globalResponseProperties;
|
||||
|
||||
public DefaultBeforeControllerAdviceProcessImpl(GlobalResponseProperties globalResponseProperties) {
|
||||
this.globalResponseProperties = globalResponseProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void call(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception e) {
|
||||
if (globalResponseProperties.isPrintExceptionInGlobalAdvice()) {
|
||||
log.error("[{}] {}", request.getMethod(), request.getRequestURI(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.response;
|
||||
|
||||
import com.feiniaojin.gracefulresponse.ExceptionAliasRegister;
|
||||
import com.feiniaojin.gracefulresponse.advice.*;
|
||||
import com.feiniaojin.gracefulresponse.advice.lifecycle.exception.BeforeControllerAdviceProcess;
|
||||
import com.feiniaojin.gracefulresponse.advice.lifecycle.exception.ControllerAdvicePredicate;
|
||||
import com.feiniaojin.gracefulresponse.advice.lifecycle.exception.RejectStrategy;
|
||||
import com.feiniaojin.gracefulresponse.api.ResponseFactory;
|
||||
import com.feiniaojin.gracefulresponse.api.ResponseStatusFactory;
|
||||
import com.feiniaojin.gracefulresponse.defaults.DefaultResponseFactory;
|
||||
import com.feiniaojin.gracefulresponse.defaults.DefaultResponseStatusFactoryImpl;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springdoc.core.parsers.ReturnTypeParser;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
import top.continew.starter.core.util.GeneralPropertySourceFactory;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* 全局响应自动配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(GlobalResponseProperties.class)
|
||||
@PropertySource(value = "classpath:default-web.yml", factory = GeneralPropertySourceFactory.class)
|
||||
public class GlobalResponseAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalResponseAutoConfiguration.class);
|
||||
private final GlobalResponseProperties globalResponseProperties;
|
||||
|
||||
public GlobalResponseAutoConfiguration(GlobalResponseProperties globalResponseProperties) {
|
||||
this.globalResponseProperties = globalResponseProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局响应体处理(非 void)
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GrNotVoidResponseBodyAdvice grNotVoidResponseBodyAdvice() {
|
||||
return new GrNotVoidResponseBodyAdvice();
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局响应体处理(void)
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GrVoidResponseBodyAdvice grVoidResponseBodyAdvice() {
|
||||
return new GrVoidResponseBodyAdvice();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理前回调(目前仅打印异常日志)
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public BeforeControllerAdviceProcess beforeControllerAdviceProcess() {
|
||||
return new DefaultBeforeControllerAdviceProcessImpl(globalResponseProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 框架异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public FrameworkExceptionAdvice frameworkExceptionAdvice(BeforeControllerAdviceProcess beforeControllerAdviceProcess,
|
||||
@Lazy RejectStrategy rejectStrategy) {
|
||||
FrameworkExceptionAdvice frameworkExceptionAdvice = new FrameworkExceptionAdvice();
|
||||
frameworkExceptionAdvice.setRejectStrategy(rejectStrategy);
|
||||
frameworkExceptionAdvice.setControllerAdviceProcessor(frameworkExceptionAdvice);
|
||||
frameworkExceptionAdvice.setBeforeControllerAdviceProcess(beforeControllerAdviceProcess);
|
||||
frameworkExceptionAdvice.setControllerAdviceHttpProcessor(frameworkExceptionAdvice);
|
||||
return frameworkExceptionAdvice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据校验异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public DataExceptionAdvice dataExceptionAdvice(BeforeControllerAdviceProcess beforeControllerAdviceProcess,
|
||||
@Lazy RejectStrategy rejectStrategy) {
|
||||
DataExceptionAdvice dataExceptionAdvice = new DataExceptionAdvice();
|
||||
dataExceptionAdvice.setRejectStrategy(rejectStrategy);
|
||||
dataExceptionAdvice.setControllerAdviceProcessor(dataExceptionAdvice);
|
||||
dataExceptionAdvice.setBeforeControllerAdviceProcess(beforeControllerAdviceProcess);
|
||||
dataExceptionAdvice.setControllerAdviceHttpProcessor(dataExceptionAdvice);
|
||||
return dataExceptionAdvice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认全局异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public DefaultGlobalExceptionAdvice defaultGlobalExceptionAdvice(BeforeControllerAdviceProcess beforeControllerAdviceProcess,
|
||||
@Lazy RejectStrategy rejectStrategy) {
|
||||
DefaultGlobalExceptionAdvice advice = new DefaultGlobalExceptionAdvice();
|
||||
advice.setRejectStrategy(rejectStrategy);
|
||||
CopyOnWriteArrayList<ControllerAdvicePredicate> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
|
||||
copyOnWriteArrayList.add(advice);
|
||||
advice.setPredicates(copyOnWriteArrayList);
|
||||
advice.setControllerAdviceProcessor(advice);
|
||||
advice.setBeforeControllerAdviceProcess(beforeControllerAdviceProcess);
|
||||
advice.setControllerAdviceHttpProcessor(advice);
|
||||
return advice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认参数校验异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public DefaultValidationExceptionAdvice defaultValidationExceptionAdvice(BeforeControllerAdviceProcess beforeControllerAdviceProcess,
|
||||
@Lazy RejectStrategy rejectStrategy) {
|
||||
DefaultValidationExceptionAdvice advice = new DefaultValidationExceptionAdvice();
|
||||
advice.setRejectStrategy(rejectStrategy);
|
||||
advice.setControllerAdviceProcessor(advice);
|
||||
advice.setBeforeControllerAdviceProcess(beforeControllerAdviceProcess);
|
||||
// 设置默认参数校验异常http处理器
|
||||
advice.setControllerAdviceHttpProcessor(advice);
|
||||
return advice;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝策略
|
||||
*/
|
||||
@Bean
|
||||
public RejectStrategy rejectStrategy() {
|
||||
return new DefaultRejectStrategyImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public ExceptionHandlerExceptionResolver releaseExceptionHandlerExceptionResolver() {
|
||||
return new ReleaseExceptionHandlerExceptionResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* 国际化支持
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.WEB_RESPONSE, name = "i18n", havingValue = "true")
|
||||
public GrI18nResponseBodyAdvice grI18nResponseBodyAdvice() {
|
||||
return new GrI18nResponseBodyAdvice();
|
||||
}
|
||||
|
||||
/**
|
||||
* 国际化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = PropertiesConstants.WEB_RESPONSE, name = "i18n", havingValue = "true")
|
||||
public MessageSource messageSource() {
|
||||
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
|
||||
messageSource.setBasenames("i18n", "i18n/messages");
|
||||
messageSource.setDefaultEncoding("UTF-8");
|
||||
messageSource.setDefaultLocale(Locale.CHINA);
|
||||
return messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应工厂
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ResponseFactory responseBeanFactory() {
|
||||
return new DefaultResponseFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应状态工厂
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ResponseStatusFactory responseStatusFactory() {
|
||||
return new DefaultResponseStatusFactoryImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常别名注册
|
||||
*/
|
||||
@Bean
|
||||
public ExceptionAliasRegister exceptionAliasRegister() {
|
||||
return new ExceptionAliasRegister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应支持
|
||||
*/
|
||||
@Bean
|
||||
public AdviceSupport adviceSupport() {
|
||||
return new AdviceSupport();
|
||||
}
|
||||
|
||||
/**
|
||||
* SpringDoc 全局响应处理器
|
||||
*
|
||||
* @return {@link ApiDocGlobalResponseHandler }
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnClass(ReturnTypeParser.class)
|
||||
@ConditionalOnMissingBean
|
||||
public ApiDocGlobalResponseHandler apiDocGlobalResponseHandler() {
|
||||
return new ApiDocGlobalResponseHandler(globalResponseProperties);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Web-Global Response' completed initialization.");
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.response;
|
||||
|
||||
import com.feiniaojin.gracefulresponse.GracefulResponseProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
|
||||
/**
|
||||
* 全局响应配置属性
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.5.0
|
||||
*/
|
||||
@ConfigurationProperties(PropertiesConstants.WEB_RESPONSE)
|
||||
public class GlobalResponseProperties extends GracefulResponseProperties {}
|
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.server;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务器配置属性
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@ConfigurationProperties("server.extension")
|
||||
public class ServerExtensionProperties {
|
||||
|
||||
/**
|
||||
* 默认禁止三个不安全的 HTTP 方法(如 CONNECT、TRACE、TRACK)
|
||||
*/
|
||||
private static final List<String> DEFAULT_ALLOWED_METHODS = List.of("CONNECT", "TRACE", "TRACK");
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 不允许的请求方式
|
||||
*/
|
||||
private List<String> disallowedMethods = new ArrayList<>(DEFAULT_ALLOWED_METHODS);
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public List<String> getDisallowedMethods() {
|
||||
return disallowedMethods;
|
||||
}
|
||||
|
||||
public void setDisallowedMethods(List<String> disallowedMethods) {
|
||||
this.disallowedMethods = disallowedMethods;
|
||||
}
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.autoconfigure.server;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import io.undertow.Undertow;
|
||||
import io.undertow.server.handlers.DisallowedMethodsHandler;
|
||||
import io.undertow.util.HttpString;
|
||||
import top.continew.starter.core.constant.PropertiesConstants;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Undertow 自动配置
|
||||
*
|
||||
* @author Jasmine
|
||||
* @author Charles7c
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(Undertow.class)
|
||||
@EnableConfigurationProperties(ServerExtensionProperties.class)
|
||||
@ConditionalOnProperty(prefix = "server.extension", name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||
public class UndertowAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UndertowAutoConfiguration.class);
|
||||
|
||||
/**
|
||||
* Undertow 自定义配置
|
||||
*/
|
||||
@Bean
|
||||
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> customize(ServerExtensionProperties properties) {
|
||||
return factory -> {
|
||||
factory.addDeploymentInfoCustomizers(deploymentInfo -> deploymentInfo
|
||||
.addInitialHandlerChainWrapper(handler -> new DisallowedMethodsHandler(handler, properties
|
||||
.getDisallowedMethods()
|
||||
.stream()
|
||||
.map(HttpString::tryFromString)
|
||||
.collect(Collectors.toSet()))));
|
||||
log.debug("[ContiNew Starter] - Disallowed HTTP methods on Server Undertow: {}.", properties
|
||||
.getDisallowedMethods());
|
||||
log.debug("[ContiNew Starter] - Auto Configuration 'Web-Server Undertow' completed initialization.");
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.model;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.feiniaojin.gracefulresponse.api.ResponseStatusFactory;
|
||||
import com.feiniaojin.gracefulresponse.data.Response;
|
||||
import com.feiniaojin.gracefulresponse.data.ResponseStatus;
|
||||
import com.feiniaojin.gracefulresponse.defaults.DefaultResponseStatus;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 响应信息
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Schema(description = "响应信息")
|
||||
public class R<T> implements Response {
|
||||
|
||||
private static final ResponseStatusFactory RESPONSE_STATUS_FACTORY = SpringUtil
|
||||
.getBean(ResponseStatusFactory.class);
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
@Schema(description = "状态码", example = "0")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 状态信息
|
||||
*/
|
||||
@Schema(description = "状态信息", example = "ok")
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
@Schema(description = "是否成功", example = "true")
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
@Schema(description = "时间戳", example = "1691453288000")
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
@Schema(description = "响应数据")
|
||||
private T data;
|
||||
|
||||
/**
|
||||
* 状态信息
|
||||
*/
|
||||
private ResponseStatus status = new DefaultResponseStatus();
|
||||
|
||||
public R() {
|
||||
}
|
||||
|
||||
public R(ResponseStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public R(String code, String msg) {
|
||||
this.setCode(code);
|
||||
this.setMsg(msg);
|
||||
}
|
||||
|
||||
public R(ResponseStatus status, T data) {
|
||||
this(status);
|
||||
this.setData(data);
|
||||
}
|
||||
|
||||
public R(String code, String msg, T data) {
|
||||
this(code, msg);
|
||||
this.setData(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(ResponseStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public ResponseStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPayload(Object payload) {
|
||||
this.data = (T)payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public Object getPayload() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return status.getCode();
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
status.setCode(code);
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return status.getMsg();
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
status.setMsg(msg);
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return Objects.equals(RESPONSE_STATUS_FACTORY.defaultSuccess().getCode(), status.getCode());
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作成功
|
||||
*
|
||||
* @return R /
|
||||
*/
|
||||
public static R ok() {
|
||||
return new R(RESPONSE_STATUS_FACTORY.defaultSuccess());
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作成功
|
||||
*
|
||||
* @param data 响应数据
|
||||
* @return R /
|
||||
*/
|
||||
public static R ok(Object data) {
|
||||
return new R(RESPONSE_STATUS_FACTORY.defaultSuccess(), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作成功
|
||||
*
|
||||
* @param msg 业务状态信息
|
||||
* @param data 响应数据
|
||||
* @return R /
|
||||
*/
|
||||
public static R ok(String msg, Object data) {
|
||||
R r = ok(data);
|
||||
r.setMsg(msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作失败
|
||||
*
|
||||
* @return R /
|
||||
*/
|
||||
public static R fail() {
|
||||
return new R(RESPONSE_STATUS_FACTORY.defaultError());
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作失败
|
||||
*
|
||||
* @param code 业务状态码
|
||||
* @param msg 业务状态信息
|
||||
* @return R /
|
||||
*/
|
||||
public static R fail(String code, String msg) {
|
||||
return new R(code, msg);
|
||||
}
|
||||
}
|
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.util;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 文件工具类
|
||||
*
|
||||
* @author Zheng Jie(<a href="https://gitee.com/elunez/eladmin">ELADMIN</a>)
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class FileUploadUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(FileUploadUtils.class);
|
||||
|
||||
private FileUploadUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*
|
||||
* @param multipartFile 源文件对象
|
||||
* @param filePath 文件路径
|
||||
* @param isKeepOriginalFilename 是否保留原文件名
|
||||
* @return 目标文件对象
|
||||
*/
|
||||
public static File upload(MultipartFile multipartFile, String filePath, boolean isKeepOriginalFilename) {
|
||||
String originalFilename = multipartFile.getOriginalFilename();
|
||||
String extensionName = FileNameUtil.extName(originalFilename);
|
||||
String fileName;
|
||||
if (isKeepOriginalFilename) {
|
||||
fileName = "%s-%s.%s".formatted(FileNameUtil.getPrefix(originalFilename), DateUtil.format(LocalDateTime
|
||||
.now(), DatePattern.PURE_DATETIME_MS_PATTERN), extensionName);
|
||||
} else {
|
||||
fileName = "%s.%s".formatted(IdUtil.fastSimpleUUID(), extensionName);
|
||||
}
|
||||
try {
|
||||
String pathname = filePath + fileName;
|
||||
File dest = new File(pathname).getCanonicalFile();
|
||||
// 如果父路径不存在,自动创建
|
||||
if (!dest.getParentFile().exists() && (!dest.getParentFile().mkdirs())) {
|
||||
log.error("Create upload file parent path failed.");
|
||||
}
|
||||
// 文件写入
|
||||
multipartFile.transferTo(dest);
|
||||
return dest;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param file 文件
|
||||
*/
|
||||
public static void download(HttpServletResponse response, File file) throws IOException {
|
||||
download(response, new FileInputStream(file), file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param inputStream 文件流
|
||||
* @param fileName 文件名
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public static void download(HttpServletResponse response,
|
||||
InputStream inputStream,
|
||||
String fileName) throws IOException {
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLUtil.encode(fileName));
|
||||
try (inputStream; var outputStream = response.getOutputStream()) {
|
||||
response.setContentLengthLong(inputStream.transferTo(outputStream));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.util;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 可重复读取请求体的包装器
|
||||
* 支持文件流直接透传,非文件流可重复读取
|
||||
*
|
||||
* @author echo
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public class RepeatReadRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private byte[] cachedBody;
|
||||
private final HttpServletRequest originalRequest;
|
||||
|
||||
public RepeatReadRequestWrapper(HttpServletRequest request) throws IOException {
|
||||
super(request);
|
||||
this.originalRequest = request;
|
||||
|
||||
// 判断是否为文件上传请求
|
||||
if (!isMultipartContent(request)) {
|
||||
this.cachedBody = IoUtil.readBytes(request.getInputStream(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
// 如果是文件上传,直接返回原始输入流
|
||||
if (isMultipartContent(originalRequest)) {
|
||||
return originalRequest.getInputStream();
|
||||
}
|
||||
|
||||
// 非文件上传,返回可重复读取的输入流
|
||||
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
|
||||
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return byteArrayInputStream.available() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
// 非阻塞I/O,这里可以根据需要实现
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return byteArrayInputStream.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() throws IOException {
|
||||
// 如果是文件上传,直接返回原始Reader
|
||||
if (isMultipartContent(originalRequest)) {
|
||||
new BufferedReader(new InputStreamReader(originalRequest.getInputStream(), StandardCharsets.UTF_8));
|
||||
}
|
||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为文件上传请求
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 是否为文件上传请求
|
||||
*/
|
||||
public boolean isMultipartContent(HttpServletRequest request) {
|
||||
return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart/");
|
||||
}
|
||||
}
|
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.util;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.WriteListener;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 可重复读取响应内容的包装器
|
||||
* 支持缓存响应内容,便于日志记录和后续处理 (不缓存SSE)
|
||||
*
|
||||
* @author echo
|
||||
* @author Charles7c
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public class RepeatReadResponseWrapper extends HttpServletResponseWrapper {
|
||||
|
||||
private final ByteArrayOutputStream cachedOutputStream = new ByteArrayOutputStream();
|
||||
private final PrintWriter writer = new PrintWriter(cachedOutputStream, true);
|
||||
/**
|
||||
* 是否为流式响应
|
||||
*/
|
||||
private boolean isStreamingResponse = false;
|
||||
|
||||
public RepeatReadResponseWrapper(HttpServletResponse response) {
|
||||
super(response);
|
||||
checkStreamingResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentType(String type) {
|
||||
super.setContentType(type);
|
||||
// 根据 Content-Type 判断是否为流式响应
|
||||
if (type != null) {
|
||||
String lowerType = type.toLowerCase();
|
||||
isStreamingResponse = lowerType.contains(MediaType.TEXT_EVENT_STREAM_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStreamingResponse() {
|
||||
String contentType = getContentType();
|
||||
if (contentType != null) {
|
||||
String lowerType = contentType.toLowerCase();
|
||||
isStreamingResponse = lowerType.contains(MediaType.TEXT_EVENT_STREAM_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() throws IOException {
|
||||
checkStreamingResponse();
|
||||
// 对于 SSE 流式响应,直接返回原始响应流,不做额外处理
|
||||
if (isStreamingResponse) {
|
||||
return super.getOutputStream();
|
||||
}
|
||||
return new ServletOutputStream() {
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener writeListener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
cachedOutputStream.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
cachedOutputStream.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
cachedOutputStream.write(b, off, len);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getWriter() throws IOException {
|
||||
checkStreamingResponse();
|
||||
if (isStreamingResponse) {
|
||||
// 对于 SSE 流式响应,直接返回原始响应写入器,不做额外处理
|
||||
return super.getWriter();
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存的响应内容
|
||||
*
|
||||
* @return 缓存的响应内容
|
||||
*/
|
||||
public String getResponseContent() {
|
||||
if (!isStreamingResponse) {
|
||||
writer.flush();
|
||||
return cachedOutputStream.toString(StandardCharsets.UTF_8);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将缓存的响应内容复制到原始响应中
|
||||
*
|
||||
* @throws IOException IO 异常
|
||||
*/
|
||||
public void copyBodyToResponse() throws IOException {
|
||||
if (!isStreamingResponse && cachedOutputStream.size() > 0) {
|
||||
getResponse().getOutputStream().write(cachedOutputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为流式响应
|
||||
*
|
||||
* @return 是否为流式响应
|
||||
*/
|
||||
public boolean isStreamingResponse() {
|
||||
return isStreamingResponse;
|
||||
}
|
||||
}
|
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.util;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.json.jackson.util.JSONUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Servlet 工具类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @author echo
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class ServletUtils extends JakartaServletUtil {
|
||||
|
||||
private ServletUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器及其版本信息
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 浏览器及其版本信息
|
||||
*/
|
||||
public static String getBrowser(HttpServletRequest request) {
|
||||
if (null == request) {
|
||||
return null;
|
||||
}
|
||||
return getBrowser(request.getHeader("User-Agent"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器及其版本信息
|
||||
*
|
||||
* @param userAgentString User-Agent 字符串
|
||||
* @return 浏览器及其版本信息
|
||||
*/
|
||||
public static String getBrowser(String userAgentString) {
|
||||
UserAgent userAgent = UserAgentUtil.parse(userAgentString);
|
||||
return userAgent.getBrowser().getName() + StringConstants.SPACE + userAgent.getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作系统
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 操作系统
|
||||
*/
|
||||
public static String getOs(HttpServletRequest request) {
|
||||
if (null == request) {
|
||||
return null;
|
||||
}
|
||||
return getOs(request.getHeader("User-Agent"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作系统
|
||||
*
|
||||
* @param userAgentString User-Agent 字符串
|
||||
* @return 操作系统
|
||||
*/
|
||||
public static String getOs(String userAgentString) {
|
||||
UserAgent userAgent = UserAgentUtil.parse(userAgentString);
|
||||
return userAgent.getOs().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求方法
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestMethod() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getMethod() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestParameter(String name) {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getParameter(name) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 Ip
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestIp() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? getClientIP(request) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求头信息
|
||||
*
|
||||
* @return {@link Map }<{@link String }, {@link String }>
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, String> getRequestHeaders() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? getHeaderMap(request) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 URL(包含 query 参数)
|
||||
* <p>{@code http://localhost:8000/system/user?page=1&size=10}</p>
|
||||
*
|
||||
* @return {@link URI }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static URI getRequestUrl() {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
String queryString = request.getQueryString();
|
||||
if (CharSequenceUtil.isBlank(queryString)) {
|
||||
return URI.create(request.getRequestURL().toString());
|
||||
}
|
||||
try {
|
||||
StringBuilder urlBuilder = appendQueryString(queryString);
|
||||
return new URI(urlBuilder.toString());
|
||||
} catch (URISyntaxException e) {
|
||||
String encoded = UriUtils.encodeQuery(queryString, StandardCharsets.UTF_8);
|
||||
StringBuilder urlBuilder = appendQueryString(encoded);
|
||||
return URI.create(urlBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求路径
|
||||
*
|
||||
* @return {@link URI }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestPath() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getRequestURI() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求 body 参数
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getRequestBody() {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request instanceof RepeatReadRequestWrapper wrapper && !wrapper.isMultipartContent(request)) {
|
||||
String body = JakartaServletUtil.getBody(request);
|
||||
return JSONUtils.isTypeJSON(body) ? body : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @return {@link Map }<{@link String }, {@link Object }>
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, Object> getRequestParams() {
|
||||
String body = getRequestBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtils.isTypeJSON(body)
|
||||
? JSONUtils.toBean(body, Map.class)
|
||||
: Collections.unmodifiableMap(JakartaServletUtil.getParamMap(Objects.requireNonNull(getRequest())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应状态
|
||||
*
|
||||
* @return int
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static int getResponseStatus() {
|
||||
HttpServletResponse response = getResponse();
|
||||
return response != null ? response.getStatus() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应所有的头(header)信息
|
||||
*
|
||||
* @return header值
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, String> getResponseHeaders() {
|
||||
HttpServletResponse response = getResponse();
|
||||
if (response == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
final Collection<String> headerNames = response.getHeaderNames();
|
||||
final Map<String, String> headerMap = MapUtil.newHashMap(headerNames.size(), true);
|
||||
for (String name : headerNames) {
|
||||
headerMap.put(name, response.getHeader(name));
|
||||
}
|
||||
return headerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应 body 参数
|
||||
*
|
||||
* @return {@link String }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static String getResponseBody() {
|
||||
HttpServletResponse response = getResponse();
|
||||
if (response instanceof RepeatReadResponseWrapper wrapper && !wrapper.isStreamingResponse()) {
|
||||
String body = wrapper.getResponseContent();
|
||||
return JSONUtils.isTypeJSON(body) ? body : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取响应参数
|
||||
*
|
||||
* @return {@link Map }<{@link String }, {@link Object }>
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static Map<String, Object> getResponseParams() {
|
||||
String body = getResponseBody();
|
||||
return CharSequenceUtil.isNotBlank(body) && JSONUtils.isTypeJSON(body)
|
||||
? JSONUtils.toBean(body, Map.class)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP Session
|
||||
*
|
||||
* @return HttpSession
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
HttpServletRequest request = getRequest();
|
||||
return request != null ? request.getSession() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP Request
|
||||
*
|
||||
* @return HttpServletRequest
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
ServletRequestAttributes attributes = getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return attributes.getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 HTTP Response
|
||||
*
|
||||
* @return HttpServletResponse
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
ServletRequestAttributes attributes = getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return attributes.getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求属性
|
||||
*
|
||||
* @return {@link ServletRequestAttributes }
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public static ServletRequestAttributes getRequestAttributes() {
|
||||
try {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes)attributes;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加查询字符串
|
||||
*
|
||||
* @param queryString 查询字符串
|
||||
* @return {@link StringBuilder }
|
||||
*/
|
||||
private static StringBuilder appendQueryString(String queryString) {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request == null) {
|
||||
return new StringBuilder();
|
||||
}
|
||||
return new StringBuilder().append(request.getRequestURL())
|
||||
.append(StringConstants.QUESTION_MARK)
|
||||
.append(queryString);
|
||||
}
|
||||
}
|
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.web.util;
|
||||
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.server.PathContainer;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Spring Web 工具类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.1.1
|
||||
*/
|
||||
public class SpringWebUtils {
|
||||
|
||||
private SpringWebUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径是否匹配
|
||||
*
|
||||
* @param path 路径
|
||||
* @param patterns 匹配模式列表
|
||||
* @return 是否匹配
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public static boolean isMatch(String path, List<String> patterns) {
|
||||
return patterns.stream().anyMatch(pattern -> isMatch(path, pattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径是否匹配
|
||||
*
|
||||
* @param path 路径
|
||||
* @param patterns 匹配模式列表
|
||||
* @return 是否匹配
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public static boolean isMatch(String path, String... patterns) {
|
||||
return Arrays.stream(patterns).anyMatch(pattern -> isMatch(path, pattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径是否匹配
|
||||
*
|
||||
* @param path 路径
|
||||
* @param pattern 匹配模式
|
||||
* @return 是否匹配
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public static boolean isMatch(String path, String pattern) {
|
||||
PathPattern pathPattern = PathPatternParser.defaultInstance.parse(pattern);
|
||||
PathContainer pathContainer = PathContainer.parsePath(path);
|
||||
return pathPattern.matches(pathContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册静态资源映射
|
||||
*
|
||||
* @param handlerMap 静态资源映射
|
||||
*/
|
||||
public static void deRegisterResourceHandler(Map<String, String> handlerMap) {
|
||||
ApplicationContext applicationContext = SpringUtil.getApplicationContext();
|
||||
// 获取已经注册的映射
|
||||
final HandlerMapping resourceHandlerMapping = applicationContext
|
||||
.getBean("resourceHandlerMapping", HandlerMapping.class);
|
||||
final Map<String, Object> oldHandlerMap = (Map<String, Object>)ReflectUtil
|
||||
.getFieldValue(resourceHandlerMapping, "handlerMap");
|
||||
// 移除之前注册的映射
|
||||
for (Map.Entry<String, String> entry : handlerMap.entrySet()) {
|
||||
String pathPattern = CharSequenceUtil.appendIfMissing(entry.getKey(), StringConstants.PATH_PATTERN);
|
||||
oldHandlerMap.remove(pathPattern);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册静态资源映射
|
||||
*
|
||||
* @param handlerMap 静态资源映射
|
||||
*/
|
||||
public static void registerResourceHandler(Map<String, String> handlerMap) {
|
||||
ApplicationContext applicationContext = SpringUtil.getApplicationContext();
|
||||
// 获取已经注册的映射
|
||||
final HandlerMapping resourceHandlerMapping = applicationContext
|
||||
.getBean("resourceHandlerMapping", HandlerMapping.class);
|
||||
final Map<String, Object> oldHandlerMap = (Map<String, Object>)ReflectUtil
|
||||
.getFieldValue(resourceHandlerMapping, "handlerMap");
|
||||
// 重新注册映射
|
||||
final ServletContext servletContext = applicationContext.getBean(ServletContext.class);
|
||||
final ContentNegotiationManager contentNegotiationManager = applicationContext
|
||||
.getBean("mvcContentNegotiationManager", ContentNegotiationManager.class);
|
||||
final UrlPathHelper urlPathHelper = applicationContext.getBean("mvcUrlPathHelper", UrlPathHelper.class);
|
||||
final ResourceHandlerRegistry resourceHandlerRegistry = new ResourceHandlerRegistry(applicationContext, servletContext, contentNegotiationManager, urlPathHelper);
|
||||
for (Map.Entry<String, String> entry : handlerMap.entrySet()) {
|
||||
// 移除之前注册的映射
|
||||
String pathPattern = CharSequenceUtil.appendIfMissing(CharSequenceUtil.removeSuffix(entry
|
||||
.getKey(), StringConstants.SLASH), StringConstants.PATH_PATTERN);
|
||||
oldHandlerMap.remove(pathPattern);
|
||||
// 重新注册映射
|
||||
String resourceLocations = CharSequenceUtil.appendIfMissing(entry.getValue(), StringConstants.SLASH);
|
||||
resourceHandlerRegistry.addResourceHandler(pathPattern).addResourceLocations("file:" + resourceLocations);
|
||||
}
|
||||
final Map<String, ?> additionalUrlMap = ReflectUtil
|
||||
.<SimpleUrlHandlerMapping>invoke(resourceHandlerRegistry, "getHandlerMapping")
|
||||
.getUrlMap();
|
||||
ReflectUtil.<Void>invoke(resourceHandlerMapping, "registerHandlers", additionalUrlMap);
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
top.continew.starter.web.autoconfigure.mvc.WebMvcAutoConfiguration
|
||||
top.continew.starter.web.autoconfigure.cors.CorsAutoConfiguration
|
||||
top.continew.starter.web.autoconfigure.server.UndertowAutoConfiguration
|
@@ -1,42 +0,0 @@
|
||||
--- ### 响应配置
|
||||
continew-starter.web.response:
|
||||
# 是否开启国际化(默认:false)
|
||||
i18n: false
|
||||
# 响应类全名(配置后 response-style 将不再生效)
|
||||
response-class-full-name: top.continew.starter.web.model.R
|
||||
# 自定义失败 HTTP 状态码(默认:200,建议业务和通信状态码区分)
|
||||
default-http-status-code-on-error: 200
|
||||
# 自定义成功响应码(默认:0)
|
||||
default-success-code: 0
|
||||
# 自定义成功提示(默认:ok)
|
||||
default-success-msg: ok
|
||||
# 自定义失败响应码(默认:1)
|
||||
default-error-code: 1
|
||||
# 自定义失败提示(默认:error)
|
||||
default-error-msg: error
|
||||
# 是否打印异常日志(默认:false)
|
||||
print-exception-in-global-advice: true
|
||||
# 是否将原生异常错误信息填充到状态信息中(默认:false)
|
||||
origin-exception-using-detail-message: true
|
||||
# 例外包路径(支持数字, * 和 ** 通配符匹配),该包路径下的 Controller 将被忽略处理
|
||||
exclude-packages:
|
||||
- io.swagger.**
|
||||
- org.springdoc.**
|
||||
- org.springframework.boot.actuate.*
|
||||
|
||||
--- ### 服务器配置
|
||||
server:
|
||||
## Undertow 服务器配置
|
||||
undertow:
|
||||
# HTTP POST 请求内容的大小上限(默认 -1,不限制)
|
||||
max-http-post-size: -1
|
||||
# 以下的配置会影响 buffer,这些 buffer 会用于服务器连接的 IO 操作,有点类似 Netty 的池化内存管理
|
||||
# 每块 buffer的空间大小(越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可)
|
||||
buffer-size: 512
|
||||
# 是否分配的直接内存(NIO 直接分配的堆外内存)
|
||||
direct-buffers: true
|
||||
threads:
|
||||
# 设置 IO 线程数,它主要执行非阻塞的任务,它们会负责多个连接(默认每个 CPU 核心一个线程)
|
||||
io: 8
|
||||
# 阻塞任务线程池,当执行类似 Servlet 请求阻塞操作,Undertow 会从这个线程池中取得线程(它的值设置取决于系统的负载)
|
||||
worker: 256
|
Reference in New Issue
Block a user