mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(api): return 400 on path-variable type mismatch instead of 500
This commit is contained in:
parent
c49b4a21bb
commit
8030a41125
@ -11,6 +11,7 @@ import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
import vip.mate.common.result.R;
|
||||
import vip.mate.i18n.I18nService;
|
||||
@ -97,6 +98,22 @@ public class GlobalExceptionHandler {
|
||||
return ResponseEntity.badRequest().body(R.fail(400, msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* A path variable or request parameter could not be coerced into the
|
||||
* handler's declared type (e.g. a non-numeric segment on an {@code /{id}}
|
||||
* route bound to {@code Long}). This is a malformed client request, not a
|
||||
* server fault, so it must surface as 400 — never a 500 with a full stack
|
||||
* trace from the catch-all handler below.
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public ResponseEntity<R<Void>> handleTypeMismatch(MethodArgumentTypeMismatchException e,
|
||||
HttpServletRequest request) {
|
||||
String required = e.getRequiredType() != null ? e.getRequiredType().getSimpleName() : "expected type";
|
||||
String msg = "Invalid value for parameter '" + e.getName() + "': expected " + required;
|
||||
log.warn("Argument type mismatch: {} {} - {}", request.getMethod(), request.getRequestURI(), msg);
|
||||
return ResponseEntity.badRequest().body(R.fail(400, msg));
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoResourceFoundException.class)
|
||||
public ResponseEntity<R<Void>> handleNoResourceFound(NoResourceFoundException e,
|
||||
HttpServletRequest request) {
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
package vip.mate.exception;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import vip.mate.common.result.R;
|
||||
import vip.mate.i18n.I18nService;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Verifies that a non-coercible path variable on a typed route surfaces as a
|
||||
* clean HTTP 400 (handled by {@link GlobalExceptionHandler}) instead of leaking
|
||||
* a 500 with a full stack trace from the catch-all handler.
|
||||
*/
|
||||
class GlobalExceptionHandlerTypeMismatchTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
I18nService i18n = mock(I18nService.class);
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(new ProbeController())
|
||||
.setControllerAdvice(new GlobalExceptionHandler(i18n))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Non-numeric segment on a Long {id} route returns 400, not 500.")
|
||||
void nonNumericIdReturns400() throws Exception {
|
||||
mockMvc.perform(get("/probe/status"))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value(400))
|
||||
.andExpect(jsonPath("$.msg").value("Invalid value for parameter 'id': expected Long"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("A valid numeric id still resolves the handler normally.")
|
||||
void numericIdReturns200() throws Exception {
|
||||
mockMvc.perform(get("/probe/123"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data").value(123));
|
||||
}
|
||||
|
||||
/** Minimal stand-in for any controller with a {@code Long} path variable. */
|
||||
@RestController
|
||||
static class ProbeController {
|
||||
@GetMapping("/probe/{id}")
|
||||
R<Long> probe(@PathVariable Long id) {
|
||||
return R.ok(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user