Spring MVC parameter passing summary

Keywords: Java Spring

Content type definition

MediaType, that is, Internet Media Type; It is also called MIME type. In the Http protocol message header, content type is used to represent the media type information in the specific request.

Common media formats are as follows:

  • text/html: HTML format
  • text/plain: plain text format
  • text/xml: XML format
  • image/gif: gif picture format
  • image/jpeg: jpg picture format
  • image/png: png picture format

Media format type starting with application:

  • application/xhtml+xml: XHTML format
  • application/xml: XML data format
  • application/atom+xml: Atom XML aggregation format
  • application/json: JSON data format
  • application/pdf: pdf format
  • application/msword: Word document format
  • application/octet-stream :
    Binary stream data (such as common file downloads)
  • application/x-www-form-urlencoded :
    The default encType is the form, and the form data is encoded in key/value format and sent to the server (the default format of the form submission data)

b. Content type instance description

The above is the basic definition and value. The following describes several typical methods in combination with examples
application/x-www-form-urlencoded: data is encoded as name / value pairs. This is the standard encoding format.
Multipart / form data: the data is encoded as a message, and each control on the page corresponds to a part of the message.
text/plain: data is encoded in plain text (text/json/xml/html) without any controls or formatting characters

For front-end use, the enctype attribute of the form is the encoding method. There are two common methods: application/x-www-form-urlencoded and multipart / form data. The default is application/x-www-form-urlencoded.

When using spring MVC for daily development, you may encounter various types of request parameters on the front end. Here is a relatively comprehensive summary. The interface for handling controller parameters in spring MVC is HandlerMethodArgumentResolver. This interface has many subclasses, which handle different (annotation type) parameters respectively. Only a few subclasses are listed below:

  • RequestParamMethodArgumentResolver: parameters annotated with @ RequestParam, MultipartFile type parameters and Simple type parameters (such as long, int, etc.) are used for parsing.
  • RequestResponseBodyMethodProcessor: resolves the parameters that handle the @ RequestBody annotation.
  • PathVariableMapMethodArgumentResolver: resolves the parameters that handle the @ PathVariable annotation.

In fact, HandlerMethodArgumentResolverComposite is generally used when parsing the request parameters of a controller, which loads all enabled HandlerMethodArgumentResolver subclasses. The subclass HandlerMethodArgumentResolver uses the subclass HttpMessageConverter (actually a list for traversal matching resolution) to perform matching resolution when parsing parameters, such as MappingJackson2HttpMessageConverter (using Jackson for serialization and deserialization). What the subclass of HandlerMethodArgumentResolver depends on is actually determined by the content type in the request header (uniformly named MediaType in spring MVC, see org.springframework.http.MediaType). Therefore, we must clarify what the content type of the external request is before processing the request parameters of the controller. The above logic can be seen directly from the source code AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters. The idea is relatively clear. In the @ RequestMapping annotation, the products and consumers attributes are related to the content type of the request or response:

Consumers attribute: Specifies the submitted content type for processing the request, such as application/json, text/html, etc. the request will be accepted only if the value of the corresponding content type is hit.
produces attribute: Specifies the returned content type. It is returned only when the (Accept) type in the request header of a request contains the specified type. If JSON data is returned, application / JSON is generally considered; charset=UTF-8.
In addition, Jackson is used as the JSON toolkit by default in spring MVC. If you do not fully understand the operation of the whole set of source code, it is generally not very recommended to modify the mappingjackson 2HttpMessageConverter used by default (for example, some people prefer to use fastjason to implement HttpMessageConverter and introduce fastjason as HTTP message converter, which is not recommended).

Get request

When initiating a Get request, the browser uses the application/x-www-form-urlencoded method to convert the form data into a string (key1 = value1 & key2 = Value2...) and splice it to the url. This is our common case where the url has request parameters
The RequestBody also supports the GET method?????

Post form

When initiating a post request, if the file is not transmitted, the browser also encapsulates the data of the form into k=v and throws the result into the http body. Take the form submitted by the blog of open source China as an example. For a typical post form, the uploaded data is assembled in form data, which is a kv structure
If there is a scenario of transferring files, the content type will be upgraded to multipart / form data, which will not be expanded in detail

Post json object

In addition to the previous method, there is also a common method for post forms, that is, all form data is placed in a large json string and then thrown to the back end. Here is also an online example. The goods of an e-commerce platform are published. The screenshot is as follows

Note that the Request Payload above is a large json string, which is significantly different from the previous one

Code example:
The default format of Ajax is: application/x-www-form-urlencoded, which is equivalent to (username = "admin" & password = 123) to transfer data (this is the fixed format of GET requests)
Front end code:
When Ajax uploads data in the default format, the JSON object user is directly used for data without conversion to a JSON string (very convenient)

 var user= {
                "username" : username,
                "password" : password,
                "rememberMe":rememberMe
            };
$.ajax({
    url : "http://...../jsontest.do",
    type : "POST",
    async : true,
    data : user,
    dataType : 'json',
    success : function(data) {
    }
});

The backend uses @ RequestParam annotation or omits:
[recommended]

//Omit comments directly
@RequestMapping("/jsontest.do")
public void test(User user,String username,String password,Boolean rememberMe){
    System.out.println(user);
    System.out.println("username: " + username);
    System.out.println("password: " + password);
    System.out.println("rememberMe: " + rememberMe);
    
}


//Annotate
@RequestMapping("/jsontest.do")
public void test(@RequestParam String username,
@RequestParam String password,@RequestParam Boolean rememberMe,){
    System.out.println("username: " + username);
    System.out.println("password: " + password);
    System.out.println("rememberMe: " + rememberMe);
}

advantage:
1. The data passed by the front end does not need to be converted into json string: JSON.stringify(user)
2. The parameters accepted by the back-end are flexible, that is, they can be encapsulated as User objects, single parameters username and rememberMe, or even mixed User objects and single rememberMe parameters

[object] - object type parameter reception

We then write an interface to submit user information, using the model class mentioned above, mainly including user name, age and contact information list. At this time, the final code of our target controller is as follows:

@Data
public class User {

    private String name;
    private Integer age;
    private List<Contact> contacts;
}

@Data
public class Contact {

    private String name;
    private String phone;
}
@PostMapping(value = "/user")
public User saveUser(User user) {
    log.info(user.toString());
    return user;
}

We still specify the content type as application/x-www-form-urlencoded, and then we need to construct the request parameters:

**Because no annotation is used, the final parameter processor is ServletModelAttributeMethodProcessor, which mainly encapsulates the form parameters in HttpServletRequest into the MutablePropertyValues instance, instantiates the parameter type (creates a User instance by constructing reflection), and reflects the matching attributes to fill in the values. In addition, the list attribute request parameter in the request complex parameter looks strange. In fact, it is the same as adding the parameter finally mapped to the Map type in the. properties file** So, can you submit the entire request parameters in one field?

This cannot be done directly, because the actually submitted Form key is a user string, and value is actually a string. There is no Converter of type string - > user. In fact, RequestParamMethodArgumentResolver relies on the Converter instance list in WebConversionService for parameter conversion:

There are still solutions. Add an org.springframework.core.convert.converter.Converter implementation:

@Component
public class StringUserConverter implements Converter<String, User> {

    @Autowaired
    private ObjectMapper objectMapper;

    @Override
    public User convert(String source) {
        try {
               return objectMapper.readValue(source, User.class);
            } catch (IOException e) {
               throw new IllegalArgumentException(e);
        }
    }
}

The above method belongs to the method of saving the country by curve, which is not recommended to be used in the production environment. However, if the docking of some third-party interfaces cannot avoid this parameter, this implementation method can be selected.

[array] - list or array type parameters.

It is extremely not recommended to force the use of list or array type parameters in the form of application/x-www-form-urlencoded media type form submission, unless it is compatible with the parameter submission processing of historical legacy systems.
For example, the submitted parameter form is:

list = ["string-1", "string-2", "string-3"]
The form of form parameters should be written as:
name value
list[0] string-1
list[1] string-2
list[2] string-3

The controller code is as follows:

@PostMapping(path = "/list")
public void list(@RequestParam(name="list") List<String> list) {
    log.info(list);
}

A more complex example is as follows. Suppose that the message format to be submitted is as follows:
user = [{"name":"doge-1","age": 21},{"name":"doge-2","age": 22}]

The form of form parameters should be written as:
name value
user[0].name doge-1
user[0].age 21
user[1].name doge-2
user[1].age 22

The controller code is as follows:

@PostMapping(path = "/user")
public void saveUsers(@RequestParam(name="user") List<UserVo> users) {
    log.info(users);
}

@Data
public class UserVo{

	private String name;
	private Integer age;
}

Post json string

@The RequestBody makes the REST interface no longer receive requests with content type of application/x-www-form-urlencoded, but need to display the requests specified as application/json
It is also a request to add json string format to the body. While using this method, it is more appropriate to honestly select the POST method. Parameters cannot be obtained through javax.servlet.ServletRequest#getParameter.
Code example:
Front end code:

var user= {
                "username" : username,
                "password" : password
          };
$.ajax({
        url : "http://...../jsontest.do",
        type : "POST",
        async : true,
        contentType: "application/json; charset=utf-8",
        data : JSON.stringify(user),
        dataType : 'json',
        success : function(data) {
        }
 });

The backend must use @ RequestBody annotation:

//In this way, all parameters can only be encapsulated in the User object and cannot be set separately
@RequestMapping("/jsontest")
public void test(@RequestBody User user  ){
    String username = user.getUsername();
    String password = user.getPassword();
}

perhaps

@RequestMapping("/jsontest")
public void test(@RequestBody Map map  ){
    String username = map.get("username").toString();
    String password = map.get("password").toString();
}

perhaps

 public void test(@RequestBody String jsonData) {
    JSONObject jsonObject = JSON.parseObject(jsonData);
    String username= jsonObject.getString("username");
    String username= jsonObject.getString("password");
 }

Disadvantages:
1. The front end needs to use JSON.stringify() to convert JSON objects into JSON strings
2. The backend is troublesome when accepting parameters. It is neither as simple as the first nor as flexible as the first

The postman write parameters are as follows:

The code of the back-end controller is also relatively simple:

@PostMapping(value = "/user-2")
public User saveUser2(@RequestBody User user) {
    log.info(user.toString());
    return user;
}

Because the @ RequestBody annotation is used, the final parameter processor used is RequestResponseBodyMethodProcessor. In fact, MappingJackson2HttpMessageConverter is used to convert parameter types, and the underlying layer depends on Jackson related packages. It is recommended to use this method, which is the most common and robust JSON parameter processing method.

@PathVariable

The URL path parameter, or request path parameter, is a parameter obtained based on the URL template. For example, / user/{userId} is a URL template (the parameter placeholder in the URL template is {}), and the actual requested URL is / user/1, so the userId of 1 can be obtained by matching the actual requested URL and URL template. In spring MVC, the path parameter in the URL template is called PathVariable, which corresponds to the annotation @ PathVariable, and the corresponding parameter processor is PathVariableMethodArgumentResolver. Note that the resolution of @ PathVariable is matched according to the value(name) attribute, which is independent of the order of URL parameters. Take a simple example:
The background controller is as follows:

@GetMapping(value = "/user/{name}/{age}")
public String findUser1(@PathVariable(value = "age") Integer age,
						@PathVariable(value = "name") String name) {
	String content = String.format("name = %s,age = %d", name, age);
	log.info(content);
	return content;
}

This usage is widely used in the software architecture style of representative state transfer (rest). Personally, I think this style is flexible and clear (I can fully understand the meaning and function of the interface from the URL and request method). Two relatively special usage methods are introduced below.

  • URL parameter with condition.

In fact, the path parameter supports regular expressions. For example, when we use the / sex/{sex} interface, we require that sex must be F(Female) or M(Male). Then our URL template can be defined as / sex/{sex:M|F}. The code is as follows:

@GetMapping(value = "/sex/{sex:M|F}")
public String findUser2(@PathVariable(value = "sex") String sex){
    log.info(sex);
    return sex;
}

Only / sex/F or / sex/M requests enter the findUser2() controller method. Other requests for the path prefix are illegal and will return a 404 status code.
Here is just an introduction to the simplest use of regular expressions for URL parameters. You can explore more powerful usage by yourself.

File upload#

When using POSTMAN to simulate the request for file upload, you need to select form data and submit it in POST mode:

Suppose we have a picture file named doge.jpg on disk D. now we want to upload the file through the local service interface. The code of the controller is as follows:

@PostMapping(value = "/file1")
public String file1(@RequestPart(name = "file1") MultipartFile multipartFile) {
	String content = String.format("name = %s,originName = %s,size = %d",
	multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getSize());
	log.info(content);
	return content;
}

Console output is:
name = file1,originName = doge.jpg,size = 68727

I may be a little confused about how the parameters come from. We can grab a package with filder software and have a look:

It can be seen that the main attributes of MultipartFile instances are content disposition, content type and content length respectively. In addition, InputStream is used to read the last part of the request body (byte sequence of the file). The parameter processor uses the requestpartmethodargumentresolver (remember, this parameter processor must be used when @ RequestPart and MultipartFile are used). In other cases, using @ requestparameter and MultipartFile or just MultipartFile (the name of the parameter must be consistent with the name described in the content disposition description in the POST form) can also receive the uploaded file data. It is mainly parsed and processed through the requestparametermethodargumentresolver. Its function is relatively powerful. See its supportsParameter method for details, The controller method codes in these two cases are as follows:

@PostMapping(value = "/file2")
public String file2(MultipartFile file1) {
	String content = String.format("name = %s,originName = %s,size = %d",
				file1.getName(), file1.getOriginalFilename(), file1.getSize());
	log.info(content);
	return content;
}

@PostMapping(value = "/file3")
public String file3(@RequestParam(name = "file1") MultipartFile multipartFile) {
	String content = String.format("name = %s,originName = %s,size = %d",
			multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getSize());
	log.info(content);
	return content;
}

Request header#

The value of the request header is mainly obtained through the parameters of the @ RequestHeader annotation. The parameter processor is RequestHeaderMethodArgumentResolver. The Key of the request header needs to be specified in the annotation. Simple and practical as follows:
Controller method code:

@PostMapping(value = "/header")
public String header(@RequestHeader(name = "Content-Type") String Content-Type) {
    return Content-Type;
}

Cookie#

The value of the Cookie is mainly obtained through the parameters of the @ Cookie value annotation. The parameter processor is servletcookie valuemethodargumentresolver, and the Key of the Cookie needs to be specified in the annotation. The controller method code is as follows:

@PostMapping(value = "/cookie")
public String cookie(@CookieValue(name = "JSESSIONID") String sessionId) {
	return sessionId;
}

Model type parameters#

The processor of the Model type parameter is ModelMethodProcessor. In fact, this parameter is directly returned to the Model(ModelMap type) in the ModelAndViewContainer instance. Because different interfaces and class functions are to be bridged, the callback instance is BindingAwareModelMap type, which inherits from ModelMap and implements the Model interface. for instance:

@GetMapping(value = "/model")
public String model(Model model, ModelMap modelMap) {
    log.info("{}", model == modelMap);
    return "success";
}

Note that when this interface is called, the console outputs INFO and the log content is: true. Also note that the property items added in ModelMap or Model will be attached to the HttpRequestServlet instance and brought to the page for rendering.

The following configuration is required in spring-web.xml

 <!-- annotation-driven Controller mapper and controller adapter to control@Controller handle http Request mode-->
    <mvc:annotation-driven>
        <mvc:message-converters><!-- register-defaults="true"Indicates that the default message converter is used -->
            <!-- FastJson(Spring4.2x Above version settings) -->
            <!-- use@responsebody Annotation and the return value type is String Returned when string String with double quotes"{'user':'songfs'}",The reason is that string Type changed to json String, should be in json Add a string parser before the parser-->
            <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
            <!-- FastJsonHttpMessageConverter4 send@ResponseBody Support return Map<String,Object>Equal type,It is automatically converted to json-->
            <!-- Need to return json Configuration required for produces = "application/json". No need to specify utf-8 Yes -->
            <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4">
                <!-- Add supported media types -->
                <property name="supportedMediaTypes">
                    <list>
                        <!-- You can't reverse the order here. You must write it first text/html,Otherwise IE implement AJAX Time,return JSON The download file appears -->
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                        <value>application/xml;charset=UTF-8</value>
                    </list>
                </property>
                <property name="fastJsonConfig">
                    <bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
                        <property name="features">
                            <list>
                                <value>AllowArbitraryCommas</value>
                                <value>AllowUnQuotedFieldNames</value>
                                <value>DisableCircularReferenceDetect</value>
                            </list>
                        </property>
                        <property name="dateFormat" value="yyyy-MM-dd HH:mm:ss"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

Some principle References: https://juejin.cn/post/6844903648858734600

Posted by dare87 on Sat, 20 Nov 2021 21:25:06 -0800