The Spring MVC backend gets the JSON format string for the front-end submission and switches directly to the corresponding parameter object of the control method

Source: Internet
Author: User
Tags switches

Scene:

In Web application development, Spring MVC, with the advent of performance and good scalability, resulting in the use of increasing, become a fact standard, in the daily development process, there is a very common scenario: the front end through the Ajax submission method, the submission of parameters as a JSON object string, Using the Application/json type, use @requestbody in the backend control to convert the JSON string directly to the corresponding Java object, such as:

var datastr = ' [{' id ': 1476, ' name ': ' Test '}] ';    $.ajax ({                ' ${request.contextpath}/test/jsonparam.json ',                data:datastr,                ' POST ',                 false ,                 // Set Request header information                function (data) {                    console.log (data);                     // alert (data);                 }            });

In control, we want to get the corresponding object of the JSON string directly in the processed method, such as:

@RequestMapping (value = "/jsonparam")    public  jsonobject handlejsonparam (WebRequest Request, Modelmap model, @RequestBody user user) {        System.out.println (user.getname));         = Jsonobject.parseobject ("{\" status\ ": \" Ok\ "}");         return jsonobject;    }

In Handlejsonparam, we want to enter this method, the parameter user object has been initialized, and its corresponding Id,names property has been assigned to the value of 1476 and test, reducing the JSON string and the object of the anti-serialization work, improve the efficiency of developers.

Scenario Analysis:

We know that spring MVC will do some data conversion, data formatting and data validation according to the request parameter and request type before requestmapping find the corresponding control method, so our solution is in the process of data conversion, Converts the JSON string passed from the foreground request to the corresponding object, and then binds the object to the parameters of the control method.

How to resolve:

Way One: The simplest way, spring MVC provides us with a Mappingjackson2httpmessageconverter class to help you convert from a JSON string to a Java object. We only need to configure in Requestmappinghandleradpter:

<bean class= "Org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" >< Property Name= "Messageconverters" ><list><ref bean= "Mappingjacksonhttpmessageconverter"/></list ></property></bean><bean id= "Mappingjacksonhttpmessageconverter" class= " Org.springframework.http.converter.json.MappingJackson2HttpMessageConverter "><property name=" Supportedmediatypes "><list><value>text/html;charset=utf-8</value><value>application /json;charset=utf-8</value></list></property></bean>

This method relies on the following three packages: Jackson-core (2.4.0), Jackson-databind (2.4.0), Jackson-annotations (2.4.0)

Way two:

Way a pair of all JSON requests will do so, sometimes we just need to convert the specific JSON string or we want to control the details of the conversion, You can create a class to inherit Abstracthttpmessageconverter and implement the Generichttpmessageconverter interface by overriding the Canread,support method to control the types of objects that can be serialized. and rewrite the Read method to achieve the final conversion. Look at the configuration file first:

    <mvc:annotation-driven>        <mvc:message-converters>            class= " Com.web.converter.JsonRequestMessageConverter ">                <property name=" Supportedmediatypes ">                    <list >                        <value>application/json;charset=utf-8</value>                    </list>                </property>            </bean>        </mvc:message-converters>    </mvc:annotation-driven>

here I'm using the Mvc:annotation-driven configuration, which simplifies the spring configuration, The default defaultannotationhandlermapping and Annotationmethodhandleradapter examples are registered internally, similar to the configuration effect of mode one. Where the Jsonrequestmessageconverter class is as follows:

Import Java.io.ioexception;import Java.io.outputstream;import Java.lang.reflect.type;import Java.nio.charset.charset;import Java.util.concurrent.atomic.atomicreference;import Javax.servlet.http.httpservletrequest;import Org.springframework.http.httpinputmessage;import Org.springframework.http.httpoutputmessage;import Org.springframework.http.mediatype;import Org.springframework.http.converter.abstracthttpmessageconverter;import Org.springframework.http.converter.generichttpmessageconverter;import Org.springframework.http.converter.httpmessagenotreadableexception;import Org.springframework.http.converter.httpmessagenotwritableexception;import Org.springframework.http.converter.json.jackson2objectmapperbuilder;import org.springframework.util.StringUtils ; Import Org.springframework.web.context.request.requestcontextholder;import Org.springframework.web.context.request.servletrequestattributes;import Com.alibaba.fastjson.json;import Com.fasterxml.jackson.databind.javatype;import Com.fasterxml.jackson.databind.objectmapper;/** * * @Description: JSON request is a JSON-formatted parameter directly mapped to the corresponding object, control can directly get the corresponding object * @Create time:2015 September 21 pm 3:47:55 * */public class Jsonrequestmessageconverter extends Abstracthttpmessageconverter<object > Implements generichttpmessageconverter<object> {private final static Charset UTF8 = Charset.forname ("UTF-8") ;p rivate Final static string jsonp_func_name = "Callback";p rivate Charset Charset = utf8;private string jsonpfuncname = JS Onp_func_name;private objectmapper objectmapper;public Jsonrequestmessageconverter () {Super (new MediaType (" Application "," JSON ", UTF8), New mediatype (" Application "," *+json ", UTF8)); objectmapper = Jackson2objectmapperbuilder.json (). build ();} public void Setjsonpfuncname (String jsonpfuncname) {this.jsonpfuncname = Jsonpfuncname;} public void Setcharset (Charset Charset) {this.charset = Charset;} Private HttpServletRequest Getrequest () {return (servletrequestattributes) Requestcontextholder.getrequestattributes ()). Getrequest ();}private boolean REQUESTJSONP (HttpServletRequest request) {return Request.getrequesturi (). EndsWith (". Jsonp");} Private String Getjsonpfunc (HttpServletRequest request) {String func = Request.getparameter (jsonpfuncname); return Stringutils.isempty (func)? "NULL": Func;} /** * Determine if the data submitted by the foreground request can be read with this convert * type is the parameter type marked as Requestbody in control * Contextclass is the control class for the corresponding request * mediatype is a self-sustaining request type, such as JSON, text, etc. * */@Overridepublic Boolean canRead (Type type, class<?> Contextclass, mediatype mediatype) {Javatype JA Vatype = Getjavatype (type, contextclass); atomicreference<throwable> causeref = new atomicreference<throwable> (); if ( This.objectMapper.canDeserialize (Javatype, Causeref) && CanRead (mediatype)) {return true;} Throwable cause = Causeref.get (), if (cause! = null) {String msg = "Failed to evaluate deserialization for type" + Javatyp E;if (logger.isdebugenabled ()) {Logger.warn (msg, cause);} else {Logger.warn (msg + ":" + Cause);}} return false;} Private Javatype Getjavatype (Type tyPE, class<?> contextclass) {return this.objectMapper.getTypeFactory (). Constructtype (type, contextclass);} /** * * @Description: Generic read, maps a JSON request string from the foreground to a specific parameter object * @param type * @param contextclass * @param inputmessage * @return * @throws IOException * @throws httpmessagenotreadableexception * @see Org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConve Rters (Httpinputmessage, Methodparameter, Type) * @see Org.springframework.http.converter.generichttpmessageconverter#read (Java.lang.reflect.Type, Java.lang.Class, Org.springframework.http.HttpInputMessage) * @update1: * */@Overridepublic Object read (type type, class<?> Contextclass, Httpinputmessage inputmessage) throws IOException, Httpmessagenotreadableexception {try {return This.objectMapper.readValue (Inputmessage.getbody (), This.getjavatype (type, contextclass));} catch (IOException ex) {throw new Httpmessagenotreadableexception ("Could not read JSON:" + ex.GetMessage (), ex);}} /** * * @Description: If the generic read-CanRead method returns False, the Read method in Abstracthttpmessageconverter is called. This method calls readinternal to normal jsonobject * @param clazz * @param inputmessage * @return * @throws IOException * @throws httpmessage Notreadableexception * @see org.springframework.http.converter.abstracthttpmessageconverter#readinternal ( Java.lang.Class, Org.springframework.http.HttpInputMessage) * @update1: * */@Overrideprotected Object readinternal ( class<? Extends Object> clazz, Httpinputmessage inputmessage) throws IOException, Httpmessagenotreadableexception {try { Return This.objectMapper.readValue (Inputmessage.getbody (), This.getjavatype (clazz, null));} catch (IOException ex) {throw new Httpmessagenotreadableexception ("Could not read JSON:" + ex.getmessage (), ex);}} /** * * @Description: Defines the type of object that this convert supports output, that is, the type returned by the control side, which supports JSON-formatted strings and Jsonobject/jsonarray objects * @param clazz * @return * @see Org.springframework.http.converter.abstracthttpmessageconverter#supports (java.lang.Class) * @UPDATE1: * */@Overrideprotected Boolean supports (class<?> clazz) {//Handle only string/jsonobject/returned by control Jsonarray Object return String.class.isAssignableFrom (clazz) | | JSON.class.isAssignableFrom (clazz);} /** * * @Description: Defines a JSON-formatted string and Jsonobject/jsonarray object that this convert can output, and is a JSON request type * @param clazz * @param mediatype * @ return * @see Org.springframework.http.converter.abstracthttpmessageconverter#canwrite (Java.lang.Class, Org.springframework.http.MediaType) * @update1: * */@Overridepublic Boolean canWrite (class<?> clazz, mediatype    MediaType) {return This.supports (clazz) && canWrite (mediatype); }/** * * @Description: * @param t * @param outputmessage * @throws IOException * @throws httpmessagenotwritableexception * @seeorg. Springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConve Rters (T, Methodparameter, Servletserverhttprequest, servletserverhttpresponse) * @see Org.springframework.http.converter.AbstractHttpMessagEconverter#writeinternal (Java.lang.Object, org.springframework.http.HttpOutputMessage) * @update1: * */@ overrideprotected void Writeinternal (Object t, Httpoutputmessage outputmessage) throws IOException, httpmessagenotwritableexception {//Enter T value can only be string or jsonobject/jsonarray format outputstream out = Outputmessage.getbody (); HttpServletRequest request = Getrequest (); StringBuilder buffer = new StringBuilder (), Boolean REQUESTJSONP = Requestjsonp (Request), if (REQUESTJSONP) { Buffer.append (Getjsonpfunc (Request)). Append (' (');} Buffer.append (resolvejsonstring (t)); if (REQUESTJSONP) {buffer.append (");");} System.out.println ("Jsonvonvert:" +buffer); byte[] bytes = buffer.tostring (). GetBytes (charset); out.write (bytes); o Ut.flush ();} Private String resolvejsonstring (Object t) throws Httpmessagenotwritableexception{if (T instanceof JSON) {return (JSON) T). toJSONString ();} else if (t instanceof String) {return (string) t;} throw new Httpmessagenotreadableexception ("Not Json Object");}}

Way three:

Using the @initbinder tag to specify the JSON string to the processing class for conversion between specific Java object types requires an understanding of the class details and is not recommended.

problems that may occur :

There are two issues that may occur:

The problem 1:control end can get jsonobject, but can not be generic to the specific object, such as the above configuration if you get a list<user>, then the parameter from the control method to see the list inside is not the User object, But Jsonobject;

Issue 2: Sometimes garbled input or output JSON data.

Cause of the problem:

From front-end requests to back-end processing, Requestmappinghandleradapter needs to find the corresponding method through requestmapping and parse the parameters before the method is processed, such as:

In the Resolverargument method body, the following processing is done:

By readwithmessageconverters the transmitted JSON string through the configured convert class into a specific object, such as a JSON string to jsonobject, in the resulting object through the binder object to the generic type, such as Jsonobject to the user object, so if there is a problem 1, that is not converted to the user object, the binder handling problems, if there is a problem 2, the resolution of the Arg object is resolved, and when parsing this object, according to the pre-configured convert object, Indicates that there was a problem reading the data stream from request during the conversion.

How to resolve:

Solve the problem: your own conversion class must implement the Generichttpmessageconverter interface, and in the Read method to handle the JSON string to the user object conversion;

Problem two solution: each converter need to explicitly specify the supported mediatype, such as:

 Public Jsonrequestmessageconverter () {        supernew mediatype ("Application", "*+ JSON ", UTF8));         = Jackson2objectmapperbuilder.json (). build ();    }

Convert the default character set of iso-8859, so if garbled, you need to explicitly specify the character encoding in the configuration file, such as:

class= "Com.letv.shop.demoWeb.web.converter.JsonRequestMessageConverter" >                <property name= " Supportedmediatypes ">                    <list>                        <value>application/json;charset=utf-8</value>                    </list>                </property>            </bean>

It is also important to note that for the above configuration, if the AJAX request through JS, you need to add a line of configuration:

<value>application/javascript;charset=UTF-8</value>Especially in IE, because the default JS request under IE will not take CharSet, so the resolution is the default iso-8859, need to explicitly specify the encoding, such as Utf-8. Spring MVC has a sophisticated design, if there is a problem, you can through the source of constant debugging come in, meticulous patience, then the problem naturally can be done.

The Spring MVC backend gets the JSON format string for the front-end submission and switches directly to the corresponding parameter object of the control method

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.