瑞吉外卖知识点总结

目录

1.静态资源映射

2.检查用户是否登录

2.1过滤器

2.2拦截器

3.全局异常捕获

4.配置MybatisPlus分页插件

5.获取请求参数的注解

6.精度丢失之消息转换器

7.通过ThreadLocal获取登录用户的id信息

8.公共字段自动填充

9.文件上传和下载

9.1文件上传

9.2文件下载

1.静态资源映射

        创建一个mvc框架的配置类:WebMvcConfig,继承WebMvcConfigurationSupport。重写addResourceHandlers方法。

/**
 * 设置静态资源映射:springboot项目默认访问resource/static包下的静态页面,
 * 通过下面的方法,可以改变静态资源的映射路径
 * @param registry
 */
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
	//这样可以不用把backend包放在static包下,就可以被访问到
	registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
	registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}

2.检查用户是否登录

        如果用户未进行登录就访问其他页面时,对该操作进行拦截。

2.1过滤器

(1)创建自定义过滤器;(2)在启动类上加@ServletComponentScan,开启扫描;

(3)完善过滤器逻辑。

        ①获取本次请求的URI;②判断本次请求是否需要处理;③如果不需要,则直接放行;④如果需要,判断登录状态,如果已经登录,放行;⑤如果未登录则返回未登录结果。

@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
	//路径匹配器
	public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;

		//获取本次请求的URI;
		String requestURI = request.getRequestURI();
		//不被拦截的路径
		String[] urls = new String[]{
				"/employee/login",
				"/employee/logout",
				"/backend/**",
				"/front/**"
		};
		//判断本次请求是否需要处理;
		boolean check = check(urls, requestURI);
	    //如果不需要,则直接放行;
		if (check){
			filterChain.doFilter(request,response);
			return;
		}
		//如果需要,判断登录状态,如果已经登录,放行。
		Long employeeId = (Long) request.getSession().getAttribute("employeeId");
		if (employeeId != null){
            //使用ThreadLocal管理登录用户id,详情见第7小结
			BaseContext.setCurrentId(employeeId);
			filterChain.doFilter(request,response);
			return;
		}
		//如果未登录则返回未登录结果,通过输出流方式向用户页面响应数据。
		response.getWriter().write(JSON.toJSONString(Result.error("NOTLOGIN")));

		return;
	}
	
	/**
	 * 检查本次请求是否需要放行
	 * @param urls 需要放行的路径数组
	 * @param requestURI 需要进行检查的路径
	 * @return true不需要被拦截,false需要被拦截
	 */
	public boolean check(String[] urls, String requestURI){
		for (String url : urls){
			//true 匹配上了,说明不需要被拦截,false需要被拦截
			boolean match = PATH_MATCHER.match(url, requestURI);
			if (match)
				return match;
		}
		return false;
	}
}

2.2拦截器

(1)创建一个登录配置类LoginConfig,实现WebMvcConfigurer接口

@Configuration
public class LoginConfig implements WebMvcConfigurer {
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//注册登录拦截器
		InterceptorRegistration interceptor = registry.addInterceptor(new LoginInterceptor());

		//添加拦击拦截的路径,所有都拦截
		interceptor.addPathPatterns("/**");

		//添加不被拦截的路径
		interceptor.excludePathPatterns(
				"/employee/login",
				"/employee/logout",
				"/backend/**",
				"/front/**"
		);
	}
}

(2)创建登录拦截器类LoginInterceptor,实现HandlerInterceptor接口,并实现三个方法:preHandle、postHandle、afterCompletion。

public class LoginInterceptor implements HandlerInterceptor {
	//在请求处理之前被调用(controller方法调用之前)
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		Long id = (Long) request.getSession().getAttribute("id");
		if (id != null){
			BaseContext.setCurrentId(id);
			return true;
		}
		response.getWriter().write(JSON.toJSONString(Result.error("NOTLOGIN")));
		return false;
	}
	//在请求处理之后被调用
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
	}
	//整个请求结束之后被调用
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
	}
}

3.全局异常捕获

@Slf4j                            //拦截加了RestController和Controller注解的类
@RestControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptionHandler{
	//异常处理
	@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
	public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
		log.error(ex.getMessage());
		if (ex.getMessage().contains("Duplicate entry"))
			return Result.error("用户名已经存在,请修改");
		return Result.error("未知错误");
	}
}

4.配置MybatisPlus分页插件

@Configuration
public class MybatisPlusConfig {
	@Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor(){
		MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
		mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
		return mybatisPlusInterceptor;
	}
}

5.获取请求参数的注解

注解一:@PathVariable。例如localhost:8080/user/2,使用该注解获取数据。

@GetMapping(/user/{id})
public void getUserById(@PathVariable(“id”) Integer id)

注解二:@RequestParam。例如localhost:8080/user?id=2,使用该注解获取数据。

@GetMapping("/user")
public void getUserById(@RequestParam(“id”) Integer id)

注解三:@RequestBody。该注解专门用于请求体,get请求无法使用

@PostMapping("/login")
public void login(@RequestBody Employee employee)

6.精度丢失之消息转换器

        vue处理超过16位数字精度时存在精度丢失问题,所以在进行数据传输的时候需要先将传输超过16位的数据转换为String,再以json的格式传输。

方法一:在存在精度丢失问题的实体类的字段上添加@JsonSerialize(using = ToStringSerializer.class)

    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

方法二:①提供对象转换器JacksonObjectMapper,基于Jackson进行java对象到json数据的转换;②在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此转化器中使用提供的对象转换器进行java对象到json数据的转换

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
	@Override
	protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		//创建消息转换器对象
		MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
		//设置对象转换器,底层使用jackson将java对象转为json
		messageConverter.setObjectMapper(new JacksonObjectMapper());
		//将上面消息转换器对象追加到mvc框架的转换器集合中
		converters.add(0,messageConverter);
	}
}

7.通过ThreadLocal获取登录用户的id信息

        在自定义元数据处理器之前需要注意,updateUser和createUser字段值,是根据登录账号用户的id进行赋值的,而在一开始,程序就将账户的id值放在了session中,因此我们可以获取sess ion中的数据,来对两个字段进行赋值。但是在我们自定义的元数据处理中,不能获得session对象。所以需要使用ThreadLocal来解决此问题。在客户端每次发送http请求时,对应的在服务器都会分配一个新的线程来处理。其中,在处理的过程中,①LoginCheckFilter的doFilter方法、②EmployeeController的insert或update方法、③MyMetaObjectHandler的insertFill或updateFill方法属于同一个进程。在客户观发送请求后,后端会对发送的请求先进行过滤,然后进行添加或修改,最后进行公共字段自动填充操作。

        ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。常用方法:

public void set(T value)//设置当前线程的线程局部变量的值
public T get()//返回当前线程所对应的线程局部变量的值

         基于ThreadLocal编写BaseContext工具类,由于在许多修改和添加操作都涉及该问题,因此在过滤器获取登录用户的id,调用BaseContext工具类的setCurrentId方法,把id存放在Threadlocal中,最后在自定义的元数据处理器(MyMetaObjectHandler)中调用getCurrentId方法获取id值。

//基于ThreadLocal封装工具类,用于保存和获取登录用户的id
public class BaseContext {
	private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

	public static void setCurrentId(Long id){
		threadLocal.set(id);
	}

	public static Long getCurrentId(){
		return threadLocal.get();
	}
}

8.公共字段自动填充

        在项目中每次添加数据都要设置创建时间、创建人、修改时间和修改人等字段信息。为了简化该操作,使用MybatisPlus的公共字段自动填充功能,也就是插入或者更新的时候为指定字段赋予指定的值。好处:统一对这些字段进行处理,避免重复代码。

        ①在需要被自动填充的属性上加入@TableField注解,给fill属性赋值,指定自动填充策略 。

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;//在插入时自动填充

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;//在插入和更新时自动填充

    @TableField(fill = FieldFill.INSERT)
    private Long createUser;//在插入时自动填充

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;//在插入和更新时自动填充

        ②编写元数据对象处理器类,在此类中统一对公共填充字段进行赋值。

//元数据自定义处理器
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
	@Override//插入操作自动填充
	public void insertFill(MetaObject metaObject) {
		metaObject.setValue("createTime", LocalDateTime.now());
		metaObject.setValue("updateTime", LocalDateTime.now());

		metaObject.setValue("createUser", BaseContext.getCurrentId());
		metaObject.setValue("updateUser", BaseContext.getCurrentId());
	}

	@Override//更新操作自动填充
	public void updateFill(MetaObject metaObject) {
		metaObject.setValue("updateTime", LocalDateTime.now());
		metaObject.setValue("updateUser", BaseContext.getCurrentId());
	}
}

9.文件上传和下载

        需要在yml文件中创建上传和下载地址

reggie:
  path: D:\文件上传下载页面\

9.1文件上传

        在前端文件上传时,对应的form表单有以下要求:提交方式必须是post、采用multipart格式上传文件、input的type类型必须是file。

<form action="" enctype="multipart/form-data" method="post">
    <input type="file" name="myFile">
    <input type="submit" value="提交">
</form>

         在后端,Spring框架对文件上传功能进行了封装,只需要在Controller的方法中声明一个MultipartFile类型的参数即可上传文件。

@PostMapping("/upload")
public Result<String> upload(MultipartFile multipartFile){
   //multipartFile必须与前端file中的name保持一致
   System.out.println(multipartFile);
   return null;
}

         完整后端实现文件上传代码

@Value("${reggie.path}")//在yml文件中进行配置
private String basePath;

@PostMapping("/upload")
public Result<String> upload(MultipartFile file) throws IOException {
   //形参的file名,不能随便取,要和对应前端文件的name值对应
   //file是一个临时文件,需要转存到指定位置,否者本次请求完成后临时文件就会删除

   //获取原始的文件名
   String filename = file.getOriginalFilename();
   String suffix = filename.substring(filename.lastIndexOf("."));
   //为了防止文件重名问题,使用UUID
   String uuid = UUID.randomUUID().toString()+suffix;
   //判断文件目录是否存在
   File dir = new File(basePath);
   if (!dir.exists()){
      //目录不存在,创建目录
      dir.mkdirs();
   }
   //将临时文件,转存到指定位置
   file.transferTo(new File(basePath + uuid));
//返回文件名称,以便后续文件下载
   return Result.success(uuid);
}

9.2文件下载

        文件下载是指将文件从服务器传输到本地计算机的过程。通过浏览器进行文件下载通常有两种表现方式:①以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录。②直接在浏览器中打开:本质上是服务端将文件以流的形式写回浏览器的过程。

完整后端实现文件下载代码

@GetMapping("/download")
public void download(@RequestParam("name")String name, HttpServletResponse response) throws IOException {
   //输入流,通过输入流读取文件内容
   FileInputStream fis = new FileInputStream(basePath + name);
   //输出流,通过输出流将文件写回浏览器,在浏览器上展示图片
   ServletOutputStream os = response.getOutputStream();

   response.setContentType("image/jpeg");
   byte[] bytes = new byte[1024];
   int len = 0;
   while ((len = fis.read(bytes)) != -1){
      os.write(bytes,0,len);
      os.flush();
   }
   //关闭资源
   fis.close();
   os.close();
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值