近年来,我们一直致力于前端分离和RESTful风格,这也在我们的项目中使用。前几天有人遇到了解析JSON格式的请求数据的问题,然后就谈到了解析的方法。今天,我将写一篇文章来简单分析后台如何解析JSON格式的请求数据。
首先粘贴示例的代码:
前端
输入类型='button '值=' test JSON data ' onclick=' test JSON()'/脚本类型=' text/JavaScript '函数testjson () {$。Ajax ({type:' post ',Url :'/testjson ',content type : ' application/JSON ',datatype3360' JSON ',data: JSON。stringify({ ' name ' : ' Zhang San ' }),success3360函数(JSON结果){alert (JSON结果)} });}/脚本后台处理代码如下:
@ request mapping(value=' testJson ')public String testJson(@ request body Map name,httpersvletrequest request){ system . out . println(name);返回“jsonp”;}这里需要注意的是@RequestBody注释必须添加到参数对象中。当后台接收JSON数据时,必须使用自定义对象或Map对象,而不是JDK的简单对象(String/Integer/Long)。
接下来,我将发布我捕获的http请求:
内容类型:应用程序/JSON
这里需要注意的是,Request Payload中的格式必须与上图一致,SpringMVC的其他格式不会被解析。
好的,上面的代码可以解析一个JSON请求数据。让我们分析一下SpringMVC如何处理JSON请求。
SpringMVC处理请求的简单时序图如下:
在正常情况下,SpringMVC中的请求通常会调用方法doDispatch。我们输入这个方法并直接跳到它
mv=ha.handle(processedRequest,response,mappedhandler . gethandler());这一行,这一行上面的内容,后面会分析。
方法ha.handle会调用org . spring framework . web . servlet . MVC . method . abstracthandlemethodapter中的handle方法,这个方法很简单,就是调用handleInternal方法,代码如下:
公共最终ModelAndView句柄(HttpServletRequest请求,HttpServletResponse响应,Object处理程序)引发Exception { return handleInternal(请求,响应,(HandlerMethod)处理程序);} handleInternal方法调用org中的handleInternal方法。spring framework . web . servlet . MVC . method . annotation . request mapping handleradapter .让我们进入这个方法,看看在这个方法中做了什么:
@覆盖受保护的模型和视图句柄内部(httpersvletrequest请求,HttpServletResponse响应,HandlerMethod handlerMethod)引发异常{ ModelAndView mavcheckRequest(请求);//检查是不是所支持的请求类型、是不是要求会话//如果需要,在同步块中执行invokeHandlerMethod .如果(这个。synchronizeonsession){//会话中是不是要求同步执行HttpSession会话=请求。get session(false);如果(会话!=null){ 0对象互斥=WebUtils.getSessionMutex(会话);同步(互斥){//同步执行方法调用mav=invokeHandlerMethod(请求、响应、handlermmethod);} } else { //没有可用的HttpSession不需要互斥mav=invokeHandlerMethod(请求、响应、handlermmethod);} } else { //根本不需要会话同步.mav=invokeHandlerMethod(请求、响应、handlermmethod);//这三个调用handlermmethod调用的是同一个方法}//缓存的设置if(!回应。包含HEADER(HEADER _ CACHE _ CONtrol)){ if(GetSessionattributeshandler(HandlerMethod)).haselentattributes()){ applyCacheSeconds(响应,此。cacheseconds sforsessionattributehandlers);} else { prepareResponse(响应);} }返回mav}在上面的这个方法中我们需要关注的是调用handlermmethod这个方法invokeHandlerMethod。这个方法有点复杂,这个方法中干了很多的事,像创建数据验证类、创建方法处理类、模型视图容器等。在这里我们先忽略这些,直接跳到
invokeMETHoD。invokenandle(webRequest,MAV容器);这里。这个方法在组织。弹簧框架。网络。servlet。MVC。方法。注释。servletinocablehandlermmethod中。在这个方法中我们只关注第一句话:
对象返回值=invokeForRequest(webRequest,mavContainer,提供商AGS);invokeForRequest这个方法在组织。弹簧框架。网络。方法。支持。invoccablehandlermmethod中,同样在这个方法中我们也只关注第一句话:
object[]args=GetMethodArgumentValues(请求、mavContainer、提供商AGS);getmethodsargumentvalues从这个方法名我们可以看出来这个方法是获取方法参数值的,这个类和上面的方法在同一个类中。我们进到这个方法中看一下:
私有对象[]GetMethodArgumentValues(nativeWebRequest请求,模型和视图容器,对象.providedArgs)引发异常{ //获取参数对象数组方法中的参数都在这个对象数组中存放着方法参数[]参数=get method parameters();对象[]参数=新对象[参数。长度];for(int I=0;我参数。长度;I){ MethodParameter参数=参数[I];参数。initparameternamediscoverer(这。parameternamediscoverer);generictyperesolver。resolvereparametertype(参数,getBean().getClass());//获取参数的类型(处理参数中的泛型)args[I]=resolveprovided参数(参数,提供商AGS);//如果提供了参数的值的话,直接返回if (args[i]!=null){ 0继续;}如果(这个。argumentresolvers。supportsparameter(参数)){ //(1)支持的参数类型请尝试{ args[I]=这个。argumentresolvers。resolveargument(//(2)给参数赋值、校验的一些操作参数,mavContainer,请求,这个。数据库工厂);继续;} catch(Exception ex){ if(logger。isdebugenabled()){ logger。debug(getargumentresoluermessage(')错误解析参数,I),ex);}抛出ex;} } if(args[I]==null){ String msg=getargumenttresoluerromessage('没有适合参数的解析器,我);抛出新的illegalstatexception(msg);} }返回参数;} 我们先来看看上面的代码中(1)的地方。这个地方是给方法中的参数匹配一个合适的解析器。这个方法的真正调用的是组织。弹簧框架。网络。方法。支持。handlermethodargumentresolver复合# GetargumentResolver这个方法。
private handlermethargumentresolver getargumentresolver(方法参数){ HandlerMethodArgumentResolver结果=this。argumentresolver。获取(参数);//如果缓存中已经存在了,则从缓存中取if(result==null){ for(HandlerMethodArgumentResolver)方法argumentresolver : this。argumentresolvers){//遍历所有的参数解析器if(记录器。是否启用了跟踪()){ logger。' trace('测试参数解析器[' methodArgumentResolver ']是否支持['参数。getgenericparametertype()']');} if(方法argumentresolver。supportsparameter(参数)){//匹配合适的参数解析器并放入到缓存中结果=method argumentresolverthis . argumentresolvercache . put(参数,结果);打破;} } }返回结果;} 那SpringMVC种提供了多少参数解析器呢?看下图所示:
大概有30来个,瞬间觉得SpringMVC好强大啊,给人一种无论你在页眉里、饼干里、身体里、还是小路里,无论是什么类型的参数我都能给你解析了的霸气。我们这里的匹配到的参数解析器是组织。弹簧框架。网络。servlet。MVC。方法。注释。请求响应bymethodsprocessor这个类。我们接着来看上面代码中的(2)。解决方法这个方法真的调用的就是request responsebymethodsprocessor这个类中的解决方法的方法。我们进入到这个方法中看一下:
公共对象解析器参数(MethodParameter参数,ModelAndViewContainer MAV容器,NativeWebRequest webRequest,WebDataBinderFactory binderFactory)引发异常{ //这里是对参数的解析赋值对象arg=readWithMessageConverters(webRequest,参数,参数。getgenericparametertype());//[1]字符串名称=修道院。参数的getvariablename(参数);//获取参数校验的具体类WebDataBinder binder=binder factory。createbinder(webRequest,arg,name);if (arg!=null){ validateifapplicationable(binder,参数);//进行参数校验if (binder.getBindingResult().hasErrors()是必需的索引异常(binder,参数)){抛出新方法argumentnotvalidexception(参数,活页夹。get binding result());} } MAV容器。AddAttribute(BindingResult .模型关键字前缀名称,活页夹。getbindingsult());返回arg}我们重点看上面代码中[1]的地方。方法中的代码如下:
@覆盖受保护的测试对象读取带有消息的转换器(本机任务,方法参数方法参数,类型参数类型)引发IOException,http pediatynotsupportedexception,httpemasagenetableexception {//获取请求对象httpersvletrequest servlet请求=webrequest。getnativerequest(httprsvletrequest。类);ServletServerHttpRequest inputMessage=new ServletServerHttpRequest(servlet请求);//从请求输入流中解析出参数的值对象arg=readWithMessageConverters(inputMessage,methodParam,Param类型);if(arg==null){ if(需要检查(方法参数)){//校验参数是不是必须的引发新的HttpMessageNotReadableException(“”必需的请求正文丢失: ' methodParam.getMethod().toGenericString());} }返回参数;} 我们重点要看的是组织。弹簧框架。网络。servlet。MVC。方法。注释。abstractmessageconvertermethodargumentsolver中的readWithMessageConverters方法。
这个方法很长,在这个方法中会获取内容类型、参数的类型、方法、重新封装请求等等的操作。我们需要关注这三行代码:
inputMessage=getAdvice().beforebodread(输入消息、参数、目标类型、转换器类型);body=通用转换器。读取(目标类型、contextClass、输入消息);[1] body=getAdvice().afterBodyRead(body,inputMessage,param,targetType,转换器类型);为参数赋值的是[1]这行代码。这里调用的是组织。弹簧框架。http。转换器。JSON。摘要Jackson 2 httpmessageconverter中的阅读方法,代码如下:
@覆盖公共对象读取(类型、类?contextClass,httpinput message input message)引发ioexception,httpmessageseaable exception {/阿宽Java(Java)绿筠小姐javatype javatype=getjavatype(类型,contextClass):返回readJavaType(javaType,inputMessage)://唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟Java(Java)绿筠小姐,阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔}私有对象readjavatype(javatype javatype,httpinput消息输入消息){ try { if(映射Jackson输入消息的输入消息实例){ class?取消初始化视图=(((映射杰克逊输入消息)输入消息).get反序列化视图():如果(取消初始化视图!=null){返回这个。对象映射器。readerwin视图(取消初始化视图)。forType(javaType).read value(input message . getbody()):} }返回此。对象映射器。读取值(输入消息。get body()、javatype);//[1]沙吾提杰克逊?杰克逊贺盛瑞(音)先生(音)先生(音)先生(音)先生(音)先生(音)先生(音)先生(音)先生(音)先生)先生(音)先生(音)先生(音)先生(音)先生(音)先生),299号房体表阿叔呀,阿祖曰Java(Java)绿筠小姐} catch(IOexception ex){ 0抛出新的httpmessagenotreadableexception(“”无法读取文档: ' ex。getmessage(),ex);} } this.objectMapper.readValue贺盛德贺盛德杰克逊?杰克逊阿琼恰恰相反何曰。你说什么,贝勒爷,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,你好啊,拜占庭?拜占庭。1789年,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了,我的父亲去世了http(http)不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,不,阿云JSON!JSON!JSON!JSON335,拜占庭拜占庭JSON!JSON!JSON!JSON我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是Java(Java)柳井。
哎哎:
(法语)JSON!JSON!JSON!JSON阿云,阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆阿祖姆,范仲淹!范仲淹!范仲淹!范仲淹!范仲淹!范仲淹!范仲淹!范仲淹!范仲淹!范仲淹。绿筠小姐:
@ request mapping(value=' test JSON ')公共字符串test JSON(httpersvletrequest){ try { input stream=request。getinpertstream();bytearray输出流字节arrayoutput stream=new bytearray输出流();字节[]字节=新字节[1024];(同Internationalorganizations)国际组织标志=0;而((标志=inputStream.read(字节))0){ byteArrayOutputStream.write(字节,0标志);} System.out.println(新字符串(bytearray输出流。tobytearray()、request。getcharacterseencoding());} catch(io异常e){ e . print stack trace();}返回jsonp}什么事:
哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟:
朱塞佩朱塞佩朱塞佩朱塞佩,阿云阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜阿云娜,吴亚玲吴亚玲吴亚玲。