package com.imyeyu.spring; import com.google.gson.Gson; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.Language; import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiResponse; import com.imyeyu.java.ref.Ref; import com.imyeyu.spring.bean.RequestRange; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.beans.PropertyDescriptor; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Locale; /** * TimiSpring * *

如果使用本依赖相关组件,务必让 SpringBoot 扫描本依赖的包,在 SpringApplication 上加上注解 *

 *     @ComponentScan({"自己扫描的包", "com.imyeyu.spring"})
 * 
* * @author 夜雨 * @version 2021-11-20 17:16 */ public class TimiSpring { /** 版本号 */ public static final String VERSION = "0.0.1"; private static final Logger log = LoggerFactory.getLogger(TimiSpring.class); private static final Gson GSON = new Gson(); /** * 工具类禁止实例化 */ private TimiSpring() { } /** * 回调数据 * * @param response 返回 * @param resp 返回结果 */ public static void render(HttpServletResponse response, TimiResponse resp) { try { HttpSession session = getSession(); response.setContentType("application/json;charset=UTF-8"); OutputStream out = response.getOutputStream(); out.write(GSON.toJson(resp).getBytes(StandardCharsets.UTF_8)); out.flush(); out.close(); } catch (Exception e) { log.error("TimiSpring.render Error", e); } } /** * 回调错误 * * @param response 返回 * @param code 代码 * @param msgKey 消息映射键 */ public static void renderError(HttpServletResponse response, TimiCode code, String msgKey) { try { response.setContentType("application/json;charset=UTF-8"); OutputStream out = response.getOutputStream(); out.write(GSON.toJson(code.toResponse().msg(msgKey)).getBytes(StandardCharsets.UTF_8)); out.flush(); out.close(); } catch (Exception e) { log.error("TimiSpring.renderError error", e); } } /** * 获取 Servlet 请求属性 * * @return Servlet 请求属性 * @throws TimiException 请求异常 */ public static ServletRequestAttributes getServletRequestAttributes() { if (RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes sra) { return sra; } throw new TimiException(TimiCode.ERROR_NPE_VARIABLE); } /** * 获取 HttpServlet 请求 * * @return HttpServlet 请求 */ public static HttpServletRequest getRequest() { return getServletRequestAttributes().getRequest(); } /** * 获取请求域名 * * @return 请求域名 */ public static String getDomain() { return getRequest().getServerName(); } /** * 获取完整域名(含协议与端口) * * @return 完整域名 */ public static String getFullDomain() { HttpServletRequest req = getRequest(); String port = req.getServerPort() == 80 || req.getServerPort() == 443 ? "" : ":" + req.getServerPort(); return "%s://%s%s".formatted(req.getScheme(), getDomain(), port); } /** * 获取请求 URL * * @return 请求 URL */ public static String getURL() { return getRequest().getRequestURL().toString(); } /** * 获取请求 URI * * @return 请求 URI */ public static String getURI() { return getRequest().getRequestURI(); } /** * 从 URI 指定标记开始截取 * * @param flag 标记 * @return 截取后的 URI */ public static String cutURIStartAt(String flag) { int indexOf = getURI().indexOf(flag); TimiException.requiredTrue(-1 < indexOf, "not found flag: %s".formatted(flag)); return getURI().substring(indexOf + flag.length()); } /** * 获取 HttpServlet 回调 * * @return HttpServlet 回调 */ public static HttpServletResponse getResponse() { return getServletRequestAttributes().getResponse(); } /** * 获取 Http 会话 * * @return Http 会话 */ public static HttpSession getSession() { return getRequest().getSession(); } /** * 获取请求地理区域 * * @return 地区 */ public static Locale getLocale() { return getRequest().getLocale(); } /** * 获取请求头属性 * * @param key 属性键 * @return 属性值 */ public static String getHeader(String key) { return getRequest().getHeader(key); } /** * 获取会话数据 * * @param key 键 * @return 值 */ public static Object getSessionAttr(String key) { return getSession().getAttribute(key); } /** * 获取会话数据 * * @param key 键 * @return 值 */ public static boolean hasSessionAttr(String key) { return getSessionAttr(key) != null; } /** * 获取会话数据(字符串) * * @param key 键 * @return 值 */ public static String getSessionAttrAsString(String key) { Object sessionAttr = getSessionAttr(key); if (sessionAttr == null) { return null; } return sessionAttr.toString(); } /** * 获取会话数据 * * @param key 键 * @param clazz 值类型 * @param 值类型 * @return 值 */ public static T getSessionAttr(String key, Class clazz) { return clazz.cast(getSessionAttr(key)); } /** * 设置会话数据 * * @param key 键 * @param t 值 * @param 值类型 */ public static void setSessionAttr(String key, T t) { getSession().setAttribute(key, t); } /** * 移除会话数据 * * @param key 键 */ public static void removeSessionAttr(String key) { getSession().removeAttribute(key); } /** * 获取请求数据 * * @param key 键 * @return 值 */ public static Object getRequestAttr(String key) { return getRequest().getAttribute(key); } /** * 获取请求数据 * * @param key 键 * @return 值 */ public static boolean hasRequestAttr(String key) { return getRequestAttr(key) != null; } /** * 获取请求数据(字符串) * * @param key 键 * @return 值 */ public static String getRequestAttrAsString(String key) { Object reqAttr = getRequestAttr(key); if (reqAttr == null) { return null; } return reqAttr.toString(); } /** * 获取请求数据 * * @param key 键 * @param clazz 值类型 * @param 值类型 * @return 值 */ public static T getRequestAttr(String key, Class clazz) { return clazz.cast(getRequestAttr(key)); } /** * 设置请求数据 * * @param key 键 * @param t 值 * @param 值类型 */ public static void setRequestAttr(String key, T t) { getRequest().setAttribute(key, t); } /** * 移除请求数据 * * @param key 键 */ public static void removeRequestAttr(String key) { getRequest().removeAttribute(key); } /** * 获取请求 URL 参数 * * @param key 键 * @return 参数值 */ public static String getRequestArg(String key) { return getRequest().getParameter(key); } /** * 获取请求 URL 参数(多值) * * @param key 键 * @return 参数值 */ public static String[] getRequestArgs(String key) { return getRequest().getParameterValues(key); } /** * 添加 Cookie * * @param cookie Cookie */ public static void addCookie(Cookie cookie) { getResponse().addCookie(cookie); } /** * 添加 Cookie * * @param key 键 * @param value 值 */ public static void addCookie(String key, String value) { addCookie(new Cookie(key, value)); } /** * 获取 Cookie * * @param key 键 * @return Cookie */ public static Cookie getCookie(String key) { Cookie[] cookies = getRequest().getCookies(); if (cookies == null) { return null; } for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals(key)) { return cookies[i]; } } return null; } /** * 获取请求令牌,键为 Token 或 token,包括请求头和 URI * * @return 令牌 */ public static String getToken() { return TimiJava.firstNotEmpty(getHeader("Token"), getHeader("token"), getRequestArg("token"), getRequestArg("Token")); } /** * 获取原始语言头 * * @return 语言头 */ public static String getLanguageRaw() { return getHeader("Accept-Language"); } /** * 获取客户端地区语言 * * @return 客户端地区语言 */ public static Language.Enum getLanguage() { String name = getRequestArg("lang"); if (TimiJava.isEmpty(name)) { name = getLanguageRaw(); } if (TimiJava.isNotEmpty(name)) { List rangeList = Locale.LanguageRange.parse(name); for (Locale.LanguageRange item : rangeList) { if (item.getRange().contains("-")) { name = item.getRange(); break; } } } if (TimiJava.isNotEmpty(name)) { name = name.replace("-", "_"); } if (TimiJava.isEmpty(name)) { // use for not support return Language.Enum.zh_CN; } return Ref.toType(Language.Enum.class, name); } /** * 获取请求 IP * * @return 请求 IP * @throws TimiException 服务异常 */ public static String getRequestIP() { String ip = getHeader("x-forwarded-for"); // nginx 转发 if (TimiJava.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = getHeader("X-Forwarded-For"); } if (TimiJava.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = getHeader("X-Real-IP"); } // 默认获取 if (TimiJava.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) { ip = getRequest().getRemoteAddr(); } // 本地 IP return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; } /** * 是否本地 IP * * @return true 为本地 IP */ public static boolean isLocalIP() { return getRequestIP().startsWith("127"); } /** * 解析 Range 请求范围 * * @param fileLength 文件长度 * @return 请求范围 * @throws IOException IO 异常 */ public static RequestRange requestRange(long fileLength) throws IOException { HttpServletResponse resp = getResponse(); String range = getRequestAttrAsString("Range"); if (range == null || !range.startsWith("bytes=")) { return null; } // 处理 bytes=0-999 格式 String rangeValue = range.substring("bytes=".length()); String[] ranges = rangeValue.split("-"); TimiException.requiredTrue(2 == ranges.length, "Invalid Range format"); long start = Long.parseLong(ranges[0]); long end = ranges[1].isEmpty() ? fileLength - 1 : Long.parseLong(ranges[1]); // 验证范围有效性 if (start < 0 || fileLength <= end || end < start) { resp.setHeader("Content-Range", "bytes */" + fileLength); resp.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } return new RequestRange(start, end); } public static void copyPropertiesNotNull(Object source, Object target) { BeanWrapper srcBean = new BeanWrapperImpl(source); BeanWrapper targetBean = new BeanWrapperImpl(target); for (PropertyDescriptor pd : srcBean.getPropertyDescriptors()) { String propertyName = pd.getName(); Object srcValue = srcBean.getPropertyValue(propertyName); if (srcValue != null && targetBean.isWritableProperty(propertyName)) { targetBean.setPropertyValue(propertyName, srcValue); } } } }