美文网首页微服务系列
SpringBoot3之Web编程

SpringBoot3之Web编程

作者: 知了一笑 | 来源:发表于2023-08-08 08:13 被阅读0次

    标签:Rest.拦截器.swagger.测试;

    一、简介

    基于web包的依赖,SpringBoot可以快速启动一个web容器,简化项目的开发;

    web开发中又涉及如下几个功能点:

    拦截器:可以让接口被访问之前,将请求拦截到,通过对请求的识别和校验,判断请求是否允许通过;

    页面交互:对于服务端的开发来说,需要具备简单的页面开发能力,解决部分场景的需求;

    Swagger接口:通过简单的配置,快速生成接口的描述,并且提供对接口的测试能力;

    Junit测试:通过编写代码的方式对接口进行测试,从而完成对接口的检查和验证,并且可以不入侵原代码结构;

    二、工程搭建

    1、工程结构

    1.png

    2、依赖管理

    <!-- 基础框架组件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>${spring-boot.version}</version>
    </dependency>
    <!-- 接口文档组件 -->
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>${springdoc.version}</version>
    </dependency>
    <!-- 前端页面组件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
        <version>${spring-boot.version}</version>
    </dependency>
    <!-- 单元测试组件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>${spring-boot.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
    </dependency>
    

    三、Web开发

    1、接口开发

    编写四个简单常规的接口,从对资源操作的角度,也就是常说的:增Post、删Delete、改Put、查Get,并且使用了swagger注解,可以快速生成接口文档;

    @RestController
    @Tag(name = "Rest接口")
    public class RestWeb {
    
        @Operation(summary = "Get接口")
        @GetMapping("rest/get/{id}")
        public String restGet(@PathVariable Integer id) {
            return "OK:"+id;
        }
    
        @Operation(summary = "Post接口")
        @PostMapping("/rest/post")
        public String restPost(@RequestBody ParamBO param){
            return "OK:"+param.getName();
        }
    
        @Operation(summary = "Put接口")
        @PutMapping("/rest/put")
        public String restPut(@RequestBody ParamBO param){
            return "OK:"+param.getId();
        }
    
        @Operation(summary = "Delete接口")
        @DeleteMapping("/rest/delete/{id}")
        public String restDelete(@PathVariable Integer id){
            return "OK:"+id;
        }
    }
    

    2、页面交互

    对于服务端开发来说,在部分场景下是需要进行简单的页面开发的,比如通过页面渲染再去生成文件,或者直接通过页面填充邮件内容等;

    数据接口

    @Controller
    public class PageWeb {
    
        @RequestMapping("/page/view")
        public ModelAndView pageView (HttpServletRequest request){
            ModelAndView modelAndView = new ModelAndView() ;
            // 普通参数
            modelAndView.addObject("name", "cicada");
            modelAndView.addObject("time", "2023-07-12");
            // 对象模型
            modelAndView.addObject("page", new PageBO(7,"页面数据模型"));
            // List集合
            List<PageBO> pageList = new ArrayList<>() ;
            pageList.add(new PageBO(1,"第一页"));
            pageList.add(new PageBO(2,"第二页"));
            modelAndView.addObject("pageList", pageList);
            // Array数组
            PageBO[] pageArr = new PageBO[]{new PageBO(6,"第六页"),new PageBO(7,"第七页")} ;
            modelAndView.addObject("pageArr", pageArr);
            modelAndView.setViewName("/page-view");
            return modelAndView ;
        }
    }
    

    页面解析:分别解析了普通参数,实体对象,集合容器,数组容器等几种数据模型;

    <div style="text-align: center">
        <hr/>
        <h5>普通参数解析</h5>
        姓名:<span th:text="${name}"></span>
        时间:<span th:text="${time}"></span>
        <hr/>
        <h5>对象模型解析</h5>
        整形:<span th:text="${page.getKey()}"></span>
        字符:<span th:text="${page.getValue()}"></span>
        <hr/>
        <h5>集合容器解析</h5>
        <table style="margin:0 auto;width: 200px">
            <tr>
                <th>Key</th>
                <th>Value</th>
            </tr>
            <tr th:each="page:${pageList}">
                <td th:text="${page.getKey()}"></td>
                <td th:text="${page.getValue()}"></td>
            </tr>
        </table>
        <hr/>
        <h5>数组容器解析</h5>
        <table style="margin:0 auto;width: 200px">
            <tr>
                <th>Key</th>
                <th>Value</th>
            </tr>
            <tr th:each="page:${pageArr}">
                <td th:text="${page.getKey()}"></td>
                <td th:text="${page.getValue()}"></td>
            </tr>
        </table>
        <hr/>
    </div>
    

    效果图展示

    2.png

    四、拦截器

    1、拦截器定义

    通过实现HandlerInterceptor接口,完成对两个拦截器的自定义,请求在访问服务时,必须通过两个拦截器的校验;

    /**
     * 拦截器一
     */
    public class HeadInterceptor implements HandlerInterceptor {
        private static final Logger log  = LoggerFactory.getLogger(HeadInterceptor.class);
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                                 Object handler) throws Exception {
            log.info("HeadInterceptor:preHandle");
            Iterator<String> headNames = request.getHeaderNames().asIterator();
            log.info("request-header");
            while (headNames.hasNext()){
                String headName = headNames.next();
                String headValue = request.getHeader(headName);
                System.out.println(headName+":"+headValue);
            }
            // 放开拦截
            return true;
        }
        @Override
        public void postHandle(HttpServletRequest request,HttpServletResponse response,
                               Object handler, ModelAndView modelAndView) throws Exception {
            log.info("HeadInterceptor:postHandle");
        }
        @Override
        public void afterCompletion(HttpServletRequest request,HttpServletResponse response,
                                    Object handler, Exception e) throws Exception {
            log.info("HeadInterceptor:afterCompletion");
        }
    }
    
    /**
     * 拦截器二
     */
    public class BodyInterceptor implements HandlerInterceptor {
        private static final Logger log  = LoggerFactory.getLogger(BodyInterceptor.class);
        @Override
        public boolean preHandle(HttpServletRequest request,HttpServletResponse response,
                                 Object handler) throws Exception {
            log.info("BodyInterceptor:preHandle");
            Iterator<String> paramNames = request.getParameterNames().asIterator();
            log.info("request-param");
            while (paramNames.hasNext()){
                String paramName = paramNames.next();
                String paramValue = request.getParameter(paramName);
                System.out.println(paramName+":"+paramValue);
            }
            // 放开拦截
            return true;
        }
        @Override
        public void postHandle(HttpServletRequest request,HttpServletResponse response,
                               Object handler, ModelAndView modelAndView) throws Exception {
            log.info("BodyInterceptor:postHandle");
        }
        @Override
        public void afterCompletion(HttpServletRequest request,HttpServletResponse response,
                                    Object handler, Exception e) throws Exception {
            log.info("BodyInterceptor:afterCompletion");
        }
    }
    

    2、拦截器配置

    自定义拦截器之后,还需要添加到web工程的配置文件中,可以通过实现WebMvcConfigurer接口,完成自定义的配置添加;

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        /**
         * 添加自定义拦截器
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new HeadInterceptor()).addPathPatterns("/**");
            registry.addInterceptor(new BodyInterceptor()).addPathPatterns("/**");
        }
    }
    

    五、测试工具

    1、Swagger接口

    添加上述的springdoc依赖之后,还可以在配置文件中简单定义一些信息,访问IP:端口/swagger-ui/index.html即可;

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        /**
         * 接口文档配置
         */
        @Bean
        public OpenAPI openAPI() {
            return new OpenAPI()
                    .info(new Info().title("【boot-web】").description("Rest接口文档-2023-07-11")
                    .version("1.0.0"));
        }
    }
    
    3.png

    2、Junit测试

    在个人的习惯上,Swagger接口文档更偏向在前后端对接的时候使用,而Junit单元测试更符合开发的时候使用,这里是对RestWeb中的接口进行测试;

    @RunWith(SpringRunner.class)
    @SpringBootTest
    @AutoConfigureMockMvc
    public class RestWebTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void testGet () throws Exception {
            // GET接口测试
            MvcResult mvcResult = mockMvc
                    .perform(MockMvcRequestBuilders.get("/rest/get/1"))
                    .andReturn();
            printMvcResult(mvcResult);
        }
    
        @Test
        public void testPost () throws Exception {
            // 参数模型
            JsonMapper jsonMapper = new JsonMapper();
            ParamBO param = new ParamBO(null,"单元测试",new Date()) ;
            String paramJson = jsonMapper.writeValueAsString(param) ;
            // Post接口测试
            MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/rest/post")
                    .contentType(MediaType.APPLICATION_JSON)
                    .accept(MediaType.APPLICATION_JSON).content(paramJson)).andReturn();
            printMvcResult(mvcResult);
        }
    
        @Test
        public void testPut () throws Exception {
            // 参数模型
            JsonMapper jsonMapper = new JsonMapper();
            ParamBO param = new ParamBO(7,"Junit组件",new Date()) ;
            String paramJson = jsonMapper.writeValueAsString(param) ;
            // Put接口测试
            MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.put("/rest/put")
                    .contentType(MediaType.APPLICATION_JSON)
                    .accept(MediaType.APPLICATION_JSON).content(paramJson)).andReturn();
            printMvcResult(mvcResult);
        }
    
        @Test
        public void testDelete () throws Exception {
            // Delete接口测试
            MvcResult mvcResult = mockMvc
                    .perform(MockMvcRequestBuilders.delete("/rest/delete/2"))
                    .andReturn();
            printMvcResult(mvcResult);
        }
    
        /**
         * 打印【MvcResult】信息
         */
        private void printMvcResult (MvcResult mvcResult) throws Exception {
            System.out.println("请求-URI【"+mvcResult.getRequest().getRequestURI()+"】");
            System.out.println("响应-status【"+mvcResult.getResponse().getStatus()+"】");
            System.out.println("响应-content【"+mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8)+"】");
        }
    }
    

    六、参考源码

    文档仓库:
    https://gitee.com/cicadasmile/butte-java-note
    
    源码仓库:
    https://gitee.com/cicadasmile/butte-spring-parent
    

    相关文章

      网友评论

        本文标题:SpringBoot3之Web编程

        本文链接:https://www.haomeiwen.com/subject/pwtnpdtx.html