The use of the framework can save development time, but sometimes because of hidden some implementation details, resulting in the underlying principle of unknown, encountered problems do not know which level to solve. Therefore, I specifically recorded the following typical problem of the investigation and resolution process for reference.
The thing is, we originally had a mobile call Comment API, was developed on the net platform a few years ago, after porting to Java, found that the Android version of the app will not be able to publish the Chinese character review.
The Java version of API based on SPRINGMVC is basically the following, and it is found that the key content parameter, the controller layer, checks the result is empty.
@RequestMapping (value = "/test.api") public Object Test (httpservletrequest request,httpservletresponse response, @RequestParam (value = "content", Required = False, defaultvalue= "") String content) { //here, the value of the content is null
}
The form data of the post is checked with Charles grab packet, there is a field content, and there is a Chinese character value. But check its raw data incredibly for this form: content=%u611f%u53d7%u4e00%u4e0b%u8d85%u4eba%u7684%u808c%u8089%uff0c
We know that the current Java commonly used in the Urlencoder class, the general conversion of Chinese characters to "%xy" form, XY is two-bit 16 binary value, does not appear after%u followed by 4 characters this situation.
%u beginning represents this is a Unicode encoding format, and the following four characters are two-byte Unicode four-bit 16-based codes. In Charles Software, this decoding is supported, so you can see the Chinese characters in the packet capture data normally.
However, we have uniformly specified the encoding as UTF-8 from the SPRINGMVC framework level, and according to @requestparam annotations, it is necessary to use the UTF-8 to decode the content parameter, which leads to the loss of the content field of post.
And the Android team confirmed that in the past they did use their own unique encode method to encode the post data:
Public StaticString Urlencodeunicode (FinalString s) { if(s = =NULL) { return NULL; } Final intLength =s.length (); FinalStringBuilder Builder =NewStringBuilder (length);//Buffer for(inti = 0; i < length; i++) { Final CharCH =S.charat (i); if(ch & 0xff80) = = 0) { if(Utils.issafe (ch)) {builder.append (CH); } Else if(ch = = ") {builder.append (+); } Else{builder.append ("%"); Builder.append (Utils.inttohex (ch>> 4) & 15)); Builder.append (Utils.inttohex (ch& 15)); } } Else{builder.append ("%u"); Builder.append (Utils.inttohex (ch>>) & 15)); Builder.append (Utils.inttohex (ch>> 8) & 15)); Builder.append (Utils.inttohex (ch>> 4) & 15)); Builder.append (Utils.inttohex (ch& 15)); } } returnbuilder.tostring (); }
The reasons for this are not verified, and the Android team has decided to discard the encoding in a future release, using Java's commonly used encoder class for UTF-8 encoding. After the problem has been fixed, it is decided that the new API must be compatible with both the old and the newer encoding methods.
But at present Springmvc's @requestparam annotation is responsible for the decoding of the request data, from which layer we cut into, intercept the request data, judge its encoding method, and dynamically choose different decoding way to deal with it?
After debugging, think of the following two ways is feasible.
Method 1 To solve the problem:
Modify API interface form, discard @requestparam annotations, use @requestbody annotations, get the raw data of Post request directly, decode independently at controller layer
@RequestMapping (value = "/test.api") public Object Test ( httpservletrequest request, httpservletresponse response, @RequestBody String body) { // Here, you can get the following raw data: id= 185904&content=%u611f%u53d7%u4e00%u4e0b // can parse and decode raw data on its own (the specific decoding method is temporarily not considered) }
Method 2 To solve the problem:
By customizing the form of the filter, the getInputStream () of the request is obtained in the Dofilter () method, raw data can be obtained, and the request can be saved through the setattribute () method after parsing.
But this operation process and @RequestBody annotations, to change the Web. XML plus filter configuration, and more trouble, not necessarily used.
But one thing that needs special attention is that getInputStream () is a one-time action that, once executed, is ineffective if used elsewhere. Refer to the following code for comments:
PackageORG.JIAGOUSHI.API.AOP;ImportJava.io.BufferedReader;Importjava.io.IOException;ImportJava.io.InputStream;ImportJava.io.InputStreamReader;ImportJava.util.Map;ImportJavax.servlet.Filter;ImportJavax.servlet.FilterChain;ImportJavax.servlet.FilterConfig;Importjavax.servlet.ServletException;Importjavax.servlet.ServletRequest;ImportJavax.servlet.ServletResponse;Importjavax.servlet.http.HttpServletRequest;ImportJavax.servlet.http.HttpServletResponse; Public classMyfilterImplementsFilter {protectedFilterconfig Filterconfig; String encoding=NULL; Public voiddestroy () { This. Filterconfig =NULL; } /*** Initialize*/ Public voidInit (filterconfig filterconfig) { This. Filterconfig =Filterconfig; } /*** Filter Processing*/ Public voidDoFilter (ServletRequest servletrequest, Servletresponse servletresponse, Filterchain chain)throwsIOException, servletexception {//String s = servletrequest.getinputstream ()String line = ""; StringBuilder Body=NewStringBuilder (); intCounter = 0; InputStream stream; Stream=Servletrequest.getinputstream (); //read the data content of the post submissionBufferedReader reader =NewBufferedReader (NewInputStreamReader (stream)); while(line = Reader.readline ())! =NULL) { if(Counter > 0) {body.append ("\ r \ n"); } body.append (line); Counter++; } //the raw data for the POST request can be obtainedSystem.out.println (body); //Note: Because Servletrequest.getinputstream () has been called 1 times, it will not be called later. //the following getparametermap () and GetParameter () methods are essentially going to getinputstream (),
So we can't get any more parameters.HttpServletRequest request =(httpservletrequest) ServletRequest; HttpServletResponse Response=(HttpServletResponse) servletresponse; Map Map=Request.getparametermap (); String v= Request.getparameter ("Content"); //continue execution of the next filter, no next filter executes the requestChain.dofilter (request, response); }}
For the above filter to take effect, you need to add the following configuration in Web. XML, note the filter invocation order and the sequential migration of the write in Web. xml
<Filter> <Filter-name>Myfilter</Filter-name> <Filter-class>Org.jiagoushi.api.aop.MyFilter</Filter-class> </Filter> <filter-mapping> <Filter-name>Myfilter</Filter-name> <Url-pattern>/test.api</Url-pattern> </filter-mapping>
The investigation of this issue is over, welcome to the discussion.
SPRINGMVC unable to get the parameters in the request to investigate and resolve the problem