Java实现mock server

Mock

Java实现mock有两种方式:

  • servlet的Filter功能

  • spring的HandlerInterceptor

Filter和HandlerInterceptor的区别:

这两者在功能方面很类似,但是在具体技术实现方面,差距还是比较大的。过滤器和拦截器都属于面向切面编程的具体实现。而两者的主要区别包括以下几个方面:

  • Filter是基于函数回调(doFilter()方法)的,而Interceptor则是基于Java反射的(AOP思想)。

  • Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。

  • Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行。

  • Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。

Filter

Filter可以认为是Servlet的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:

Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

Filter有如下几个用处:

  • 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。

  • 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。

  • 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。

  • 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

Filter有如下几个种类:

  • 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。

  • 日志Filter:详细记录某些特殊的用户请求。

  • 负责解码的Filter:包括对非标准编码的请求解码。

  • 能改变XML内容的XSLT Filter等。

  • Filter可以负责拦截多个请求或响应;一个请求或响应也可以被多个Filter拦截。

创建Filter必须实现javax.servlet.Filter接口,在该接口中定义了如下三个方法:

  • void init(FilterConfig config):用于完成Filter的初始化。

  • void destory():用于Filter销毁前,完成某些资源的回收。

  • void doFilter(ServletRequest request,ServletResponse response,FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理。该方法可以实现对用户请求进行预处理(ServletRequest request),也可实现对服务器响应进行后处理(ServletResponse response)—它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。

实际代码如下:

自定义filter

@WebFilter(urlPatterns = "/mock/*",filterName = "fmsMockFilter")
@Slf4j
public class MockFilter implements Filter {
    @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;
        HttpServletResponse response=(HttpServletResponse) servletResponse;
        log.info("对"+request.getRequestURL()+"进行mock");
        // 设置跨域
        setCorsHeader(request,response);
        // mock的主要逻辑
        handle(request,response);
    }
​
    @Override
    public void destroy() {
​
    }
​
    /**
     * 设置filter跨域
     * @param request
     * @param response
     */
    private static void setCorsHeader(HttpServletRequest request, HttpServletResponse response) {
        //
        response.setHeader("Access-Control-Allow-Origin",
                StringUtils.isNotBlank(request.getHeader("Origin"))?request.getHeader("Origin"):"*");
        // credentials
        response.setHeader("Access-Control-Allow-Credentials", "true");
        // method
        response.setHeader("Access-Control-Allow-Methods", "*");
        // age
        response.setHeader("Access-Control-Max-Age", "3600");
        // header
        response.setHeader("Access-Control-Allow-Headers",
                StringUtils.isNotBlank(request.getHeader("Access-Control-Allow-Headers"))?request.getHeader("Access-Control-Allow-Headers"):"*");
    }
​
    private static void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("utf-8");
        response.setContentType(ContentType.APPLICATION_JSON.toString());
        //处理response
        //todo 
        //1、通过req的url,查询存储的mock response、header、status、
        //2、设置response的属性为1中mock response的属性
        response.setStatus(200);
        JSONObject content=new JSONObject();
        content.put("mock_k1","mock_v1");
        content.put("mock_k2","mock_v2");
        // 设置response的content
        @Cleanup PrintWriter writer = response.getWriter();
        writer.print(content.toJSONString());
        writer.flush();
    }
}

注册filter的方式有两种:

  • 实现Filter的类不使用@WebFilter,需要手动注册filter,代码如下

    @Configuration
    public class FilterConfig {
        @Bean
        public FilterRegistrationBean registFilter() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(new MockFilter());
            registration.addUrlPatterns("/mock/*");
            registration.setName("fmsMockFilter");
            registration.setOrder(1);
            return registration;
        }
    }
  • 实现Filter的类使用@WebFilter,需要在启动类上使用@ServletComponentScan扫包,代码如下

    @SpringBootApplication
    @ServletComponentScan("com.mock.config")
    public class App {
        public static void main(String[] args) {
            SpringApplication.run(App.class, args);
        }
    }

HandlerInterceptor

1、对于管理系统,通常都需要进行身份认证、权限控制、日志、统计等操作,比如不能让用户直接就进到了后台主页,必须先经过登陆页进行登陆。

2、Spring MVC 的 org.springframework.web.servlet.HandlerInterceptor 拦截器,可以对任何的后台请求进行拦截,Spring Boot 2.x 版本中默认还会对所有静态资源一并拦截。

3、HandlerInterceptor 拦截器接口一共有3个方法:

  • preHandle 拦截处理程序的执行:在 HandlerMapping 确定适当的处理程序对象之后,且在 HandlerAdapter 调用处理程序之前调用。

  • postHandle 拦截处理程序的执行:在 HandlerAdapter 实际调用处理程序之后,且在 DispatcherServlet 呈现视图之前调用。

  • afterCompletion 请求处理完成后(即呈现视图后)回调,将在处理程序执行的任何结果上调用,从而允许适当的资源清理。

    • 注意:仅当此拦截器的 preHandle 方法已成功完成并返回 true 时才会调用! 与 postHandle 方法一样,该方法将按相反顺序在链中的每个拦截器上调用,因此第一个拦截器将是最后一个被调用的拦截器。

方法返回值:

  • true - 执行链可以执行下一个拦截器或handler本身。最终请求到controller

  • false - 拦截器已经处理了相应。请求被拦截,不会请求到controller

自定义拦截器

// 自定义拦截器,需要实现 HandlerInterceptor
public class MockHandlerInterceptor implements HandlerInterceptor{
​
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 设置跨域,statuscode。headers
        setCorsHeader(request,response);
        response.setStatus(200);
        response.setContentType(ContentType.APPLICATION_JSON.toString());
        // 设置响应内容
        JSONObject content=new JSONObject();
        content.put("mock_k1","mock_v1");
        content.put("mock_k2","mock_v2");
        @Cleanup PrintWriter writer = response.getWriter();
        writer.print(content.toJSONString());
        writer.flush();
        //false 拦截请求,不请求到controller
        return false;
    }
​
    private static void setCorsHeader(HttpServletRequest request, HttpServletResponse response) {
        //
        response.setHeader("Access-Control-Allow-Origin",
                StringUtils.isNotBlank(request.getHeader("Origin"))?request.getHeader("Origin"):"*");
        // credentials
        response.setHeader("Access-Control-Allow-Credentials", "true");
        // method
        response.setHeader("Access-Control-Allow-Methods", "*");
        // age
        response.setHeader("Access-Control-Max-Age", "3600");
        // header
        response.setHeader("Access-Control-Allow-Headers",
                StringUtils.isNotBlank(request.getHeader("Access-Control-Allow-Headers"))?request.getHeader("Access-Control-Allow-Headers"):"*");
    }
}

 

注册拦截器

@Configuration
// 注册拦截器,实现 WebMvcConfigurer
public class WebConfig implements WebMvcConfigurer {
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MockHandlerInterceptor())
                .addPathPatterns("/mock/**"); // ** 表示拦截以 /mock 开头的所有请求
    }
}

 

posted @ 2023-03-23 14:30  huiyii  阅读(324)  评论(0编辑  收藏  举报