hi和hello兩個(gè)請(qǐng)求引發(fā)的@RequestBody思考
hi和hello兩個(gè)請(qǐng)求引發(fā)的@RequestBody思考
- 描述
- 思考
- DispatcherServlet
- AbstractHandlerMethodAdapter
- RequestMappingHandlerAdapter
- ServletInvocableHandlerMethod
- InvocableHandlerMethod
- HandlerMethodArgumentResolverComposite
- RequestResponseBodyMethodProcessor
- 結(jié)論
- 驗(yàn)證
- 設(shè)置日志打印級(jí)別
- 啟動(dòng)時(shí)關(guān)鍵日志
- hi請(qǐng)求日志
- hello請(qǐng)求日志
- 心得
描述
每天多思考一點(diǎn),不斷豐富自己的知識(shí)體系。有hi和hello兩個(gè)get請(qǐng)求,他們倆有啥區(qū)別呢?
@GetMapping("/hi")
public String hi(User user) {
System.out.println("hi");
System.out.println(user.toString());
return "hi";
}
@GetMapping("/hello")
public String hello(@RequestBody User user) {
System.out.println("hello");
System.out.println(user.toString());
return "hello";
}
使用Apifox發(fā)送如下兩個(gè)請(qǐng)求:
- http://127.0.0.1:8080/hi
- http://127.0.0.1:8080/hello hi請(qǐng)求正常返回“hi”,hello請(qǐng)求則返回如下“400”錯(cuò)誤。
{
"timestamp": "2022-04-15T02:00:42.580+00:00",
"status": 400,
"error": "Bad Request",
"path": "/hello"
}
hello請(qǐng)求的后臺(tái)錯(cuò)誤信息如下:
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public java.lang.String com.example.web.test.TestController.hello(com.example.web.test.User)]
然后將hello請(qǐng)求參數(shù)改為json格式,如下所示:

然后正常返回字符串“hello”。
思考
首先從寫法看來(lái),hello請(qǐng)求多一個(gè)@RequestBody注解,并且這個(gè)注解是寫在參數(shù)里的??梢猿鴶?shù)據(jù)綁定的方向去思考。@RequestBody源碼如下:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
boolean required() default true;
}
然后我們梳理一下一個(gè)請(qǐng)求的完整的處理過程。根據(jù)SpringMvc核心架構(gòu)圖說(shuō)明,從用戶發(fā)送一個(gè)請(qǐng)求到應(yīng)答信息返回,主要有以下幾個(gè)步驟:

- 發(fā)送請(qǐng)求到控制器
- 控制器進(jìn)行分發(fā)
- 處理器進(jìn)行數(shù)據(jù)校驗(yàn)和業(yè)務(wù)邏輯調(diào)用
- service層業(yè)務(wù)處理
- 邏輯處理完成
- 封裝數(shù)據(jù)模型并返回ModelAndView
- 控制器調(diào)用視圖解析
- ViewResolver進(jìn)行視圖解析
- 控制器調(diào)用視圖渲染
- View進(jìn)行視圖渲染
- 視圖渲染完成,并返回應(yīng)答信息到用戶
- 請(qǐng)求完成
通過跟蹤斷點(diǎn),發(fā)現(xiàn)以下時(shí)間軸方法。

DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
……
// 確定當(dāng)前請(qǐng)求的 handler adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
……
// 實(shí)際的handler處理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
……
}
AbstractHandlerMethodAdapter
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
RequestMappingHandlerAdapter
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
……
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
……
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
……
invocableMethod.invokeAndHandle(webRequest, mavContainer);
……
}
ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
}
InvocableHandlerMethod
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
……
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
……
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
……
}
HandlerMethodArgumentResolverComposite
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
……
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
RequestResponseBodyMethodProcessor
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
……
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
……
}
@Override
protected Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
……
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
……
}

圖中部分找到了系統(tǒng)后臺(tái)報(bào)異常的代碼。
結(jié)論
使用@RequestBody注解的接口前置條件如下:
- 請(qǐng)求頭Content-Type必須設(shè)置為application/json
- 請(qǐng)求體不能為空
驗(yàn)證
設(shè)置日志打印級(jí)別
logging.level.root: debug
啟動(dòng)時(shí)關(guān)鍵日志
_.s.web.servlet.HandlerMapping.Mappings :
c.e.w.t.TestController:
{GET [/hi]}: hi(User)
{GET [/hello]}: hello(User)
hi請(qǐng)求日志
Received [GET /hi HTTP/1.1
User-Agent: apifox/2.1.7 (https://www.apifox.cn)
Accept: */*
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 17
username=xiaoming]
正常響應(yīng)。
hello請(qǐng)求日志
Received [GET /hello HTTP/1.1
User-Agent: apifox/2.1.7 (https://www.apifox.cn)
Content-Type: application/json
Accept: */*
Host: 127.0.0.1:8080
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 32
{
"username": "xiaoming"
}]
正常響應(yīng)。
心得
雖然結(jié)論很簡(jiǎn)單,但是收獲的是思考的過程。
身為一個(gè)程序員,但求勤勤勉勉、兢兢業(yè)業(yè),每天有所得、每事有所得。
本文僅代表作者觀點(diǎn),版權(quán)歸原創(chuàng)者所有,如需轉(zhuǎn)載請(qǐng)?jiān)谖闹凶⒚鱽?lái)源及作者名字。
免責(zé)聲明:本文系轉(zhuǎn)載編輯文章,僅作分享之用。如分享內(nèi)容、圖片侵犯到您的版權(quán)或非授權(quán)發(fā)布,請(qǐng)及時(shí)與我們聯(lián)系進(jìn)行審核處理或刪除,您可以發(fā)送材料至郵箱:service@tojoy.com





