图片上传功能
public ApiRestResponse uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
throw new K100Exception(HatuExceptionEnum.UPLOAD_NO_PICK);
}
//获取文件名
String filename = file.getOriginalFilename();
//获取文件的扩展名
String extension = filename.substring(filename.lastIndexOf("."));
//生成新的文件名,使用UUID保证文件名的唯一性
String newFileName = UUID.randomUUID().toString() + extension;
//设置文件的保存路径
String filePath = Constant.FILE_UPLOAD_PATH;
//创建目标文件对象
File destFile = new File(filePath + newFileName);
//检查父目录是否存在,不存在则创建
if (!destFile.getParentFile().exists()) {
if (!destFile.getParentFile().mkdirs()) {
throw new K100Exception(HatuExceptionEnum.MKDIR_FAILED);
}
}
//保存文件
try {
file.transferTo(destFile);
} catch (IOException ex) {
throw new K100Exception(HatuExceptionEnum.UPLOAD_FAILED);
}
try {
//获取文件路径(ip+端口+文件名),返回给前端
String url = getHost(new URI(request.getRequestURL()+""))+"/img/"+newFileName;
return ApiRestResponse.success(url);
} catch (URISyntaxException e) {
//返回失败信息
return ApiRestResponse.error(HatuExceptionEnum.UPLOAD_FAILED);
}
}
/**
* 获取自定义URI
*/
private URI getHost(URI uri) {
URI effectiveUri;
try {
effectiveUri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
} catch (URISyntaxException e) {
effectiveUri = null;
}
return effectiveUri;
}
配置映射文件,从而使保存到数据库中的路径可以访问
静态资源映射
@Configuration
public class K100WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("file:" + Constant.FILE_UPLOAD_PATH);
}
log4j2日志组件
首先在pom.xml中排除自带日志组件,再引入新的log4j2组件,不需要指定版本号
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除自带日志组件-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
接着需要对log4j2进行配置
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="fatal">
<Properties>
<!-- <Property name="baseDir" value="${sys:user.home}/logs"/>-->
<Property name="baseDir" value="S:\book\e-commerce\logs"/>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY"/>
<PatternLayoutd
pattern="[%d{MM:dd HH:mm:ss.SSS}] [%level] [%logger{36}] - %msg%n"/>
</Console>
<!--debug级别日志文件输出-->
<RollingFile name="debug_appender" fileName="${baseDir}/debug.log"
filePattern="${baseDir}/debug_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在debug及以上在info以下 -->
<ThresholdFilter level="debug"/>
<ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<!-- info级别日志文件输出 -->
<RollingFile name="info_appender" fileName="${baseDir}/info.log"
filePattern="${baseDir}/info_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在info及以上在error以下 -->
<ThresholdFilter level="info"/>
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<!-- error级别日志文件输出 -->
<RollingFile name="error_appender" fileName="${baseDir}/error.log"
filePattern="${baseDir}/error_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在error及以上 -->
<ThresholdFilter level="error"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console"/>
<AppenderRef ref="debug_appender"/>
<AppenderRef ref="info_appender"/>
<AppenderRef ref="error_appender"/>
</Root>
</Loggers>
</Configuration>
统一的相应接口
public class ApiRestResponse<T> {
private static final int OK_STATUS = 10000;
private static final String OK_MSG = "success";
private Integer statusCode;
private String msg;
private T data;
public ApiRestResponse(Integer statusCode, String msg, T data) {
this.statusCode = statusCode;
this.msg = msg;
this.data = data;
}
public ApiRestResponse(Integer statusCode, String msg) {
this.statusCode = statusCode;
this.msg = msg;
}
public ApiRestResponse() {
this(OK_STATUS, OK_MSG);
}
public static <T> ApiRestResponse<T> success() {
return new ApiRestResponse<>();
}
/**
* 成功带参
*/
public static <T> ApiRestResponse<T> success(T data) {
ApiRestResponse<T> response = new ApiRestResponse<>();
response.setData(data);
return response;
}
/**
* 处理失败
*/
public static <T> ApiRestResponse<T> error(Integer errorCode, String msg) {
return new ApiRestResponse<>(errorCode,msg);
}
public static <T> ApiRestResponse<T> error(HatuExceptionEnum exception) {
return new ApiRestResponse<>(exception.getCode(), exception.getMsg());
}
public Integer getStatusCode() {
return statusCode;
}
public void setStatusCode(Integer statusCode) {
this.statusCode = statusCode;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "ApiRestResponse{" +
"statusCode=" + statusCode +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
自定义异常
public class K100Exception extends RuntimeException{
private final Integer code;
private final String msg;
public K100Exception(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public K100Exception(HatuExceptionEnum hatuException) {
this(hatuException.getCode(),hatuException.getMsg());
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
统一处理异常
统一异常处理,分别打印相关的日志文件
@ControllerAdvice
public class GlobalExceptionHandler {
private final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public Object handleException(Exception e) {
log.error("Default Exception:", e);
return ApiRestResponse.error(HatuExceptionEnum.SYSTEM_EXCEPTION);
}
@ExceptionHandler(K100Exception.class)
@ResponseBody
public Object handleK100Exception(K100Exception e) {
log.error("K100Exception", e);
return ApiRestResponse.error(e.getCode(), e.getMsg());
}
//参数不合法异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ApiRestResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("MethodArgumentNotValidException:", e);
return handleBindResult(e.getBindingResult());
}
/**
* 把异常处理为对外暴露的提示
*/
private ApiRestResponse handleBindResult(BindingResult result) {
List<String> list = new ArrayList<>();
if (result.hasErrors()) {
for (ObjectError allError : result.getAllErrors()) {
list.add(allError.getDefaultMessage());
}
}
if (list.size() == 0) {
return ApiRestResponse.error(HatuExceptionEnum.REQUEST_PARAM_ERROR);
}
return ApiRestResponse.error(HatuExceptionEnum.REQUEST_PARAM_ERROR.getCode(), list.toString());
}
}
Md5加密处理工具类
public class Md5Utils {
public static String getMD5(String str, Integer salt) throws NoSuchAlgorithmException {
char[] cArr = str.toCharArray();
for (char c : cArr) {
c = (char) (c + salt);
}
String s = new String(cArr);
MessageDigest md5 = MessageDigest.getInstance("MD5");
return Base64.encodeBase64String(md5.digest(s.getBytes()));
}
}
参数校验
"@Valid"注解是在Java中使用的一种验证注解,用于标识一个对象需要进行有效性验证。它通常与数据验证框架(如Hibernate Validator)一起使用,以确保数据的合法性和有效性。
"@Valid"注解可以应用于以下场景:
-
方法参数验证:
public void saveUser(@Valid User user) { // 保存用户信息 }
在方法参数上添加"@Valid"注解,表示需要对传入的"user"对象进行验证。
-
方法返回值验证:
public @Valid User getUser() { // 获取用户信息 }
在方法返回值前添加"@Valid"注解,表示返回的"user"对象需要进行验证。
-
验证嵌套对象:
public class Order { private String id; @Valid private List<@Valid Item> items; // ... } public class Item { private String id; @NotNull private String name; // ... }
在嵌套对象上添加"@Valid"注解,表示需要对嵌套对象进行验证。
处理参数校验后,要对相应的异常进行处理,方便统一处理,在上文的统一异常处理中已经做了相关处理MethodArgumentNotValidException.class
统一校验管理员
设置过滤器对用户进行统一验证,判断是否进行了登录,是否有管理员权限
public class AdminFilter implements Filter {
@Autowired
UserService userService;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
User currentUser = (User) session.getAttribute(Constant.HATU_MALL_USER);
if (currentUser == null) {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) servletResponse).getWriter();
out.write("{\n"
+ " \"status\": 10007,\n"
+ " \"msg\": \"用户未登录\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
return;
}
//校验是否是管理员
boolean adminRole = userService.checkAdminRole(currentUser);
if (adminRole) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) servletResponse).getWriter();
out.write("{\n"
+ " \"status\": 10009,\n"
+ " \"msg\": \"当前用户没有管理员权限\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
}
}
@Override
public void destroy() {
}
}
之后要对过滤器进行相应的配置
@Configuration
public class AdminFilterConfig {
@Bean
public AdminFilter adminFilter() {
return new AdminFilter();
}
@Bean(name = "adminFilterConf")
public FilterRegistrationBean adminFilterConfig() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(adminFilter());
filterRegistrationBean.addUrlPatterns("/admin/list/*");
filterRegistrationBean.setName("adminFilterConf");
return filterRegistrationBean;
}
}