您好,登录后才能下订单哦!
这篇文章主要介绍“如何解决Spring MVC接口漏洞百出”,在日常操作中,相信很多人在如何解决Spring MVC接口漏洞百出问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何解决Spring MVC接口漏洞百出”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
1. 前言
在开始前请务必确认添加了Spring Boot Test相关的组件,在最新的版本中应该包含以下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency>
2. 单独测试控制层
如果我们只需要对控制层接口(Controller)进行测试,且该接口不依赖@Service、@Component等注解声明的 Spring Bean 时,可以借助@WebMvcTest来启用只针对 Web 控制层的测试,例如
@WebMvcTest class CustomSpringInjectApplicationTests { @Autowired MockMvc mockMvc; @SneakyThrows @Test void contextLoads() { mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")) .andExpect(ResultMatcher.matchAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), jsonPath("$.test", Is.is("hello")))) .andDo(MockMvcResultHandlers.print()); } }
这种方式要快的多,它只加载了应用程序的一小部分。但是如果你涉及到服务层这种方式是不凑效的,我们就需要整体测试了方了。
3. 整体测试
大多数 Spring Boot 下的接口测试是整体而又全面的测试,涉及到控制层、服务层、持久层等方方面面,所以需要加载比较完整的 Spring Boot 上下文。这时我们可以这样做,声明一个抽象的测试基类:
package cn.felord.custom; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; /** * 测试基类, * @author felord.cn */ @SpringBootTest @AutoConfigureMockMvc abstract class CustomSpringInjectApplicationTests { /** * The Mock mvc. */ @Autowired MockMvc mockMvc; // 其它公共依赖和处理方法 }
只有当@AutoConfigureMockMvc存在时MockMvc才会被注入 Spring IoC。
然后针对具体的控制层进行如下测试代码的编写:
package cn.felord.custom; import lombok.SneakyThrows; import org.hamcrest.core.Is; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** * 测试FooController. * * @author felord.cn */ public class FooTests extends CustomSpringInjectApplicationTests { /** * /foo/map接口测试. */ @SneakyThrows @Test void contextLoads() { mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")) .andExpect(ResultMatcher.matchAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), jsonPath("$.test", Is.is("bar")))) .andDo(MockMvcResultHandlers.print()); } }
4. MockMvc 测试
集成测试时,希望能够通过输入 URL 对 Controller 进行测试,如果通过启动服务器,建立 http client 进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络环境等,为了可以对 Controller 进行测试,所以引入了MockMvc。
MockMvc实现了对 Http 请求的模拟,能够直接使用网络的形式,转换到 Controller 的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。接下来我们来一步步构造一个测试的模拟请求,假设我们存在一个下面这样的接口:
@RestController @RequestMapping("/foo") public class FooController { @Autowired private MyBean myBean; @GetMapping("/user") public Map<String, String> bar(@RequestHeader("Api-Version") String apiVersion, User user) { Map<String, String> map = new HashMap<>(); map.put("test", myBean.bar()); map.put("version", apiVersion); map.put("username", user.getName()); //todo your business return map; } }
参数设定为name=felord.cn&age=18,那么对应的 HTTP 报文是这样的:
GET /foo/user?name=felord.cn&age=18 HTTP/1.1 Host: localhost:8888 Api-Version: v1
可以预见的返回值为:
{ "test": "bar", "version": "v1", "username": "felord.cn" }
事实上对接口的测试可以分为以下几步。
构建请求
构建请求由MockMvcRequestBuilders负责,他提供了请求方法(Method),请求头(Header),请求体(Body),参数(Parameters),会话(Session)等所有请求的属性构建。/foo/user接口的请求可以转换为:
MockMvcRequestBuilders.get("/foo/user") .param("name", "felord.cn") .param("age", "18") .header("Api-Version", "v1")
执行 Mock 请求
然后由MockMvc执行 Mock 请求:
mockMvc.perform(MockMvcRequestBuilders.get("/foo/user") .param("name", "felord.cn") .param("age", "18") .header("Api-Version", "v1"))
对结果进行处理
请求结果被封装到ResultActions对象中,它封装了多种让我们对 Mock 请求结果进行处理的方法。
对结果进行预期期望
ResultActions#andExpect(ResultMatcher matcher)方法负责对响应的结果的进行预期期望,看看是否符合测试的期望值。参数ResultMatcher负责从响应对象中提取我们需要期望的部位进行预期比对。
假如我们期望接口/foo/user返回的是JSON,并且 HTTP 状态为200,同时响应体包含了version=v1的值,我们应该这么声明:
ResultMatcher.matchAll(MockMvcResultMatchers.status().isOk(), MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON), MockMvcResultMatchers.jsonPath("$.version", Is.is("v1")));
JsonPath是一个强大的 JSON 解析类库,请通过其项目仓库https://github.com/json-path/JsonPath了解。
对响应进行处理
ResultActions#andDo(ResultHandler handler)方法负责对整个请求/响应进行打印或者 log 输出、流输出,由MockMvcResultHandlers工具类提供这些方法。我们可以通过以上三种途径来查看请求响应的细节。
例如/foo/user接口:
MockHttpServletRequest: HTTP Method = GET Request URI = /foo/user Parameters = {name=[felord.cn], age=[18]} Headers = [Api-Version:"v1"] Body = null Session Attrs = {} Handler: Type = cn.felord.xbean.config.FooController Method = cn.felord.xbean.config.FooController#urlEncode(String, Params) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 200 Error message = null Headers = [Content-Type:"application/json"] Content type = application/json Body = {"test":"bar","version":"v1","username":"felord.cn"} Forwarded URL = null Redirected URL = null Cookies = []
获取返回结果
如果你希望进一步处理响应的结果,也可以通过ResultActions#andReturn()拿到MvcResult类型的结果进行进一步的处理。
完整的测试过程
通常andExpect是我们必然会选择的,而andDo和andReturn在某些场景下会有用,它们两个是可选的。我们把上面的连在一起。
@Autowired MockMvc mockMvc; @SneakyThrows @Test void contextLoads() { mockMvc.perform(MockMvcRequestBuilders.get("/foo/user") .param("name", "felord.cn") .param("age", "18") .header("Api-Version", "v1")) .andExpect(ResultMatcher.matchAll(status().isOk(), content().contentType(MediaType.APPLICATION_JSON), jsonPath("$.version", Is.is("v1")))) .andDo(MockMvcResultHandlers.print()); }
这种流式的接口单元测试从语义上看也是比较好理解的,你可以使用各种断言、正例、反例测试你的接口,最终让你的接口更加健壮。
到此,关于“如何解决Spring MVC接口漏洞百出”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。