前后端接口参数详解与 Mock 配置指南
一、前端请求参数类型及 Mock 处理
1.1 URL 路径参数 (Path Parameters)
场景示例:
GET /api/users/{userId}/orders/{orderId}
Mock.js 处理:
Mock.mock(/\/api\/users\/(\d+)\/orders\/(\d+)/, 'get', (options) => {
const userId = options.url.match(/\/users\/(\d+)/)[1];
const orderId = options.url.match(/\/orders\/(\d+)/)[1];
return {
userId,
orderId,
totalPrice: Mock.Random.float(100, 1000, 2)
};
});
MSW 动态匹配:
rest.get('/api/users/:userId/orders/:orderId', (req, res, ctx) => {
const { userId, orderId } = req.params;
return res(
ctx.json({
userId: Number(userId),
orderId: Number(orderId),
status: 'processed'
})
);
});
1.2 查询参数 (Query Parameters)
场景示例:
GET /api/products?category=electronics&page=2&sort=price_desc
Mock.js 响应逻辑:
Mock.mock(/\/api\/products/, 'get', (options) => {
const query = new URLSearchParams(options.url.split('?')[1]);
return {
page: query.get('page') || 1,
data: Mock.mock({
'list|10': [{
id: '@id',
name: '@ctitle',
'price|100-5000': 1,
category: query.get('category') || 'all'
}]
})
};
});
WireMock 精确匹配:
stubFor(get(urlPathEqualTo("/api/products"))
.withQueryParam("category", equalTo("electronics"))
.withQueryParam("page", equalTo("2"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBodyFile("products/electronics_page2.json")));
1.3 请求体参数 (Body Parameters)
场景示例:
POST /api/login
Content-Type: application/json
{
"username": "admin",
"password": "P@ssw0rd"
}
MSW 处理逻辑:
rest.post('/api/login', async (req, res, ctx) => {
const { username, password } = await req.json();
if (password.length < 8) {
return res(
ctx.status(400),
ctx.json({ error: "密码长度不足8位" })
);
}
return res(
ctx.json({
token: Buffer.from(username).toString('base64'),
expiresIn: 3600
})
);
});
WireMock JSON 匹配:
stubFor(post(urlEqualTo("/api/login"))
.withRequestBody(matchingJsonPath("$.username"))
.withRequestBody(matchingJsonPath("$.password"))
.willReturn(aResponse()
.withStatus(200)
.withBody("{\"token\": \"{{randomValue length=32 type='ALPHANUMERIC'}}\"}")));
二、后端接口参数 Mock 策略
2.1 Spring Boot MockMvc 参数验证
路径参数测试:
@Test
void testGetUserById() throws Exception {
mockMvc.perform(get("/users/{id}", 123))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(123));
}
请求体验证测试:
@Test
void testCreateProduct() throws Exception {
String jsonBody = "{ \"name\":\"iPhone\", \"price\":6999 }";
mockMvc.perform(post("/products")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonBody))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.sku").exists());
}
2.2 Mockito 参数匹配技巧
基础参数匹配:
// 任意字符串参数
when(userDao.findByUsername(anyString()))
.thenReturn(new User("default"));
// 特定类型匹配
when(orderService.calculateTotal(any(Order.class)))
.thenReturn(100.0);
自定义参数验证:
ArgumentCaptor<Product> productCaptor = ArgumentCaptor.forClass(Product.class);
verify(productRepository).save(productCaptor.capture());
Product savedProduct = productCaptor.getValue();
assertThat(savedProduct.getPrice()).isBetween(100, 10000);
三、高级参数处理场景
3.1 文件上传参数
Multipart 请求 Mock:
// Spring Boot 测试示例
@Test
void testUploadAvatar() throws Exception {
MockMultipartFile file = new MockMultipartFile(
"file",
"avatar.png",
"image/png",
"<<png data>>".getBytes()
);
mockMvc.perform(multipart("/upload")
.file(file)
.param("userId", "123"))
.andExpect(status().isOk());
}
3.2 OAuth2 认证头处理
WireMock 带鉴权头的模拟:
stubFor(get(urlPathEqualTo("/api/protected"))
.withHeader("Authorization", containing("Bearer "))
.willReturn(aResponse()
.withStatus(200)
.withBody("{\"secretData\":\"TOP_SECRET\"}"));
Mockito 模拟安全上下文:
@Mock
private JwtDecoder jwtDecoder;
@Test
void testSecuredEndpoint() {
Jwt jwt = Jwt.withTokenValue("token")
.header("alg", "HS256")
.claim("sub", "user123")
.build();
when(jwtDecoder.decode(anyString())).thenReturn(jwt);
// 执行需要认证的测试逻辑
}
四、参数异常模拟
4.1 无效参数响应
Mock.js 模拟参数错误:
Mock.mock(/\/api\/search/, 'get', (options) => {
const keyword = options.url.split('keyword=')[1];
if (!keyword || keyword.length < 2) {
return {
code: 400,
error: "关键词长度需大于2个字符"
};
}
// 正常返回逻辑...
});
4.2 类型转换错误
WireMock 错误响应模板:
stubFor(get(urlPathEqualTo("/api/items"))
.withQueryParam("page", matching("[0-9]+"))
.willReturn(aResponse()
.withStatus(400)
.withBody("{\"error\":\"页码必须是数字\"}")));
五、参数调试技巧
5.1 请求日志记录
WireMock 请求捕获:
WireMock.startRecording("http://real-api.com");
List<LoggedRequest> requests = findAll(getRequestedFor(urlPathEqualTo("/api/data")));
System.out.println("捕获请求数量:" + requests.size());
5.2 动态参数生成
Mock.js 智能数据生成:
Mock.mock('/api/report', {
'data|30': [{
date: "@date('yyyy-MM-dd')",
'sales|500-2000': 1,
region: "@region",
product: "@pick(['手机','电脑','配件'])"
}]
});
六、参数文档规范建议
6.1 OpenAPI 示例片段
/api/users/{id}:
get:
parameters:
- name: id
in: path
required: true
schema:
type: integer
minimum: 1
responses:
200:
content:
application/json:
schema:
type: object
properties:
id:
type: integer
name:
type: string