Model

你一定知道Model类,在控制器中,数据会存放到Model对象中,当需要生成HTML的时候,模板引擎会根据名字来定位数据,像下图这样。

new 对象手动交给spring boot 管理 springboot的model对象_spring boot

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_02

new 对象手动交给spring boot 管理 springboot的model对象_Test_03

@RequestMapping("/model")
public String model(Model model){
    model.addAttribute("hobbies", Arrays.asList("滑雪","蹦床","滑板","冲浪"));
    return "model";
}

从广义上来说,Model指的是MVC中的M,即Model(模型)。从狭义上讲,Model就是个key-value集合。实际上,上图model方法得到的model对象就是一个 java.util.Map ,你可以将Model类型替换为Map<String, Object> ,或者ModelMap——一个实现了Model接口的java.util.HashMap。

往Model里放数据还有另外一种方式,使用ModelAndView。正如它的名字一样,ModelAndView将Model和视图名绑定在一起,作为请求处理方法的返回值。

Model的实现类:BindingAwareModelMap

ModelAndView

new 对象手动交给spring boot 管理 springboot的model对象_Test_04

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_05

new 对象手动交给spring boot 管理 springboot的model对象_spring_06

写法怎么来确定,每个人不同,只要你的选择在项目里始终如一就行。

一、基本模式

1、基本数据类型(以int为例)

在controller中写一个int参数绑定的方法

@GetMapping("/getInt")
public String getInt(int id){
    return "ID = " + id;
}

@RunWith(SpringRunner.class)
@WebMvcTest
public class DataBindGetRequestControllerTest {
    @Autowired
    MockMvc mockMvc;
    
        @Test
    public void getInt() throws Exception {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.put("id", Collections.singletonList("100"));
        params.put("name",Collections.singletonList("张三"));

        MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/getInt").params(params).accept(MediaType.APPLICATION_JSON);

        ResultActions resultActions = mockMvc.perform(requestBuilder);

        resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());

    }
    
}

总结:

1)用基本类型进行参数绑定时,就必须传入key值,且value值必须是声明的基本类型,如果页面提交的数据为null或“”的话会出现数据转换异常,因此最好使用包装类型参数

2)前端的参数名和controller的参数名保持一致时就能完成数据绑定,不一致可以使用

postman请求

new 对象手动交给spring boot 管理 springboot的model对象_java_07

@RequestParam

@GetMapping("/getInt2")
public String getInt2(@RequestParam(value = "ik") int id){
    return "IK = " + id;
}

2、包装类型参数绑定

在controller中写多个包装类型参数绑定的方法

@RequestMapping("/getPackType")
@ResponseBody
public String getPackType(String name,Long id){
    return "name = " + name + " id = " + id;
}
@Test
public void getPackType() throws Exception {
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.put("name",Collections.singletonList("张三"));
    params.put("id",Collections.singletonList("1000"));

    MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/getPackType").params(params).accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(requestBuilder);

    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());

}

总结:

1)包装类型绑定参数时和基本数据类型一样,传的key值要和里面绑定的参数名一致

2)包装类型绑定参数时参数的值可以不传为null,也可以为空

new 对象手动交给spring boot 管理 springboot的model对象_spring_08

3、自定义对象类型参数绑定

@RequestMapping("/getUser")
@ResponseBody
public User getUser(User user){
    return user;
}
@Test
    public void getUser() throws Exception {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.put("id",Collections.singletonList("1000"));
        params.put("userName",Collections.singletonList("李四"));

        MockHttpServletRequestBuilder requestBuilder =
                MockMvcRequestBuilders.get("/getUser").params(params).accept(MediaType.APPLICATION_JSON);

        ResultActions resultActions = mockMvc.perform(requestBuilder);

        resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
    }

总结:1)只要将对象的属性名和前端的input的参数名一致即可

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_09

4、自定义复合对象类型参数绑定

@Data
public class OrderForm {
    private Long id;
    private String orderSN;
    private List<Goods> goods;
}

@Data
public class Goods {
    private Long id;
    private String goodsName;
}

@RequestMapping(value = "/getOrderForm",produces = "application/json;charset=utf-8")
@ResponseBody
public OrderForm getOrderForm(OrderForm orderForm){
    return orderForm;
}


@Test
public void getOrderForm() throws Exception {
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.put("id",Collections.singletonList("1000"));
    params.put("orderSN",Collections.singletonList(UUID.randomUUID().toString()));
    params.put("goods[0].id",Collections.singletonList("1000"));
    params.put("goods[0].goodsName",Collections.singletonList("苹果手机"));
    params.put("goods[1].id",Collections.singletonList("2000"));
    params.put("goods[1].goodsName",Collections.singletonList("华为手机"));

    MockHttpServletRequestBuilder requestBuilder =
        MockMvcRequestBuilders.get("/getOrderForm").params(params).accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(requestBuilder);

    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_java_10

总结:

1)自定义复合对象类型和自定义对象类型是一样的用法

2)自定义复合对象类型前端input的参数名要使用“属性名(对象类型的属性).属性名"来命名

5、List参数绑定

@RequestMapping("/listIds")
@ResponseBody
public List<Long> listIds(@RequestParam List<Long> ids){
    return ids;
}
@Test
public void listIds() throws Exception {
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.put("ids",Collections.singletonList("1,2,3"));

    MockHttpServletRequestBuilder requestBuilder =
        MockMvcRequestBuilders.get("/listIds").params(params).accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(requestBuilder);

    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

//如果不加@RequestParam("ids")
//No primary or single unique constructor found for interface java.util.List

new 对象手动交给spring boot 管理 springboot的model对象_java_11

总结:

1)前端传送通过逗号隔开

6、接收数组参数

@RequestMapping("/arrIds")
@ResponseBody
public String[] arrIds(@RequestParam String[] ids){
    return ids;
}
@Test
public void arrIds() throws Exception {
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.put("ids",Collections.singletonList("1,2,3"));

    MockHttpServletRequestBuilder requestBuilder =
        MockMvcRequestBuilders.get("/arrIds").params(params).accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(requestBuilder);

    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_12

总结:

1)前端传送通过逗号隔开

7、Map参数绑定

@RequestMapping(value = "/getMap")
@ResponseBody
public Map<String,Object> getMap(@RequestParam  Map<String,Object> map){
    return map;
}
@Test
public void getMap() throws Exception {
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.put("id",Collections.singletonList("123"));
    params.put("name",Collections.singletonList("小黑"));
    params.put("price",Collections.singletonList("500"));

    MockHttpServletRequestBuilder requestBuilder =
        MockMvcRequestBuilders.get("/getMap").params(params).accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(requestBuilder);

    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_spring_13

总结:

1)需要RequestParam注解

8、@RequestParam注解解析

@RequestParam注解的作用有参数限制、设置参数默认值、自定义参数名称

1).参数限制(可用required=false关闭)

@RequestMapping(value = "/getName",produces = {"application/json;charset=utf-8"})
@ResponseBody
public String getName(@RequestParam(required = false,defaultValue = "笑哈哈") String name){
    return name;
}
@Test
public void getName() throws Exception {
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.put("name",Collections.singletonList("小黑"));

    MockHttpServletRequestBuilder requestBuilder =
        MockMvcRequestBuilders.get("/getName").params(params).accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(requestBuilder);

    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_spring_14

2)设置参数默认值

@GetMapping("/defaultValue")
public Integer defaultValue(@RequestParam(defaultValue = "100") Integer num){
    return num;
}

3)自定义参数名称

@RequestMapping(value = "/defaultValue",produces = {"application/json;charset=utf-8"})
@ResponseBody
public Integer defaultValue(@RequestParam(defaultValue = "100",name = "goods") Integer num){
    return num;
}

总结:

1)@RequestParam注解设置更改参数名称、设置参数限制以及设置参数默认值,可根据不同场景使用

2)@RequestParam可以多个使用

二、Json模式参数绑定

由于前后端分离以及前端的多样性,通常我们使用json数据格式进行参数/数据传递,说到json格式,就得先说一下@RequestBody,这个是使用Json模式进行参数绑定必不可少的一环

1、@RequestBody注解解析

1)@RequestBody注解的作用将json格式的数据转为java对象

2)@RequestBody常用其来处理application/json类型

3)@RequestBody接收的是一个json格式的字符串

2、基本数据类型(以int为例)

@RequestMapping(value = "/postInt",method = RequestMethod.POST)
@ResponseBody
public int postInt(int id){
    return id;
}
@RunWith(SpringRunner.class)
@WebMvcTest
public class DataBindPostRequestControllerTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    public void postInt() throws Exception {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();

        params.put("id", Collections.singletonList("100"));
        MockHttpServletRequestBuilder mockHttpServletRequestBuilder =
                MockMvcRequestBuilders.post("/postInt")
                        .params(params)
                        .accept(MediaType.APPLICATION_JSON);

        ResultActions resultActions = mockMvc.perform(mockHttpServletRequestBuilder);
        resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
    }

}

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_15

3、json模式直接绑定自定义对象类型

@RequestMapping(value = "/postUser",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public User postUser(@RequestBody User user){
    return user;
}
@Test
    public void postUser() throws Exception {
        Map<String,Object> params = new HashMap<>();
        params.put("id", "100");
        params.put("userName", "张三");

        MockHttpServletRequestBuilder mockHttpServletRequestBuilder =
                MockMvcRequestBuilders.post("/postUser")
                        .content(JSONObject.toJSONString(params))
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON);

        ResultActions resultActions = mockMvc.perform(mockHttpServletRequestBuilder);
        resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
    }

总结:

1)直接json字符串转换成javaBean,但是前端传的一定是json格式的数据

2)参数名必须和实体类定义的属性名一直才可以正确获取参数

new 对象手动交给spring boot 管理 springboot的model对象_参数绑定_16

 

4、json模式直接绑定自定义复合对象类型

@RequestMapping(value = "/postOrderForm",method = RequestMethod.POST,produces = {"application/json;charset=utf-8"})
@ResponseBody
public OrderForm postOrderForm(@RequestBody OrderForm orderForm){
    return orderForm;
}
@Test
public void postOrderForm() throws Exception {
    OrderForm orderForm = new OrderForm();
    orderForm.setId(1000L);
    orderForm.setOrderSN(UUID.randomUUID().toString());

    Goods g1 = new Goods();
    g1.setId(1000L);
    g1.setGoodsName("苹果手机");

    Goods g2 = new Goods();
    g2.setId(2000L);
    g2.setGoodsName("华为手机");

    List<Goods> goodsList = new ArrayList<>();
    goodsList.add(g1);
    goodsList.add(g2);

    orderForm.setGoods(goodsList);

    MockHttpServletRequestBuilder mockHttpServletRequestBuilder =
        MockMvcRequestBuilders.post("/postOrderForm")
        .content(JSONObject.toJSONString(orderForm))
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(mockHttpServletRequestBuilder);
    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_17

 

 

总结:

1)两者都需要参数名和实体类定义的属性名一致

2)直接获取的方式,层级的参数名需要用.指定;@RequestBody形式参数json格式需要用嵌套的形式

5、json模式绑定数组类型(一般用于批量操作)

controller使用@RequestBody+List接收数组(推荐)

@RequestMapping("/postIds")
@ResponseBody
public List<Integer> postIds(@RequestBody List<Integer> ids){
    return ids;
}
@Test
public void postIds() throws Exception {
    List<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);

    MockHttpServletRequestBuilder mockHttpServletRequestBuilder =
        MockMvcRequestBuilders.post("/postIds")
        .content(JSONObject.toJSONString(ids))
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(mockHttpServletRequestBuilder);
    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_18

总结:

1)@RequestBody获取List数组类型参数使用比较广泛

2)后续可以将list中的内容转化为String类型,以","分割

6、json模式绑定多个对象(参数类型相同)

1)使用List<E>获取

@RequestMapping(value = "/postOrderForms",method = RequestMethod.POST,produces = {"application/json;charset=utf-8"})
@ResponseBody
public List<OrderForm> postOrderForms(@RequestBody List<OrderForm> orderForms){
    return orderForms;
}

 

@Test
public void postOrderForms() throws Exception {
    List<OrderForm> orderForms = new ArrayList<>();

    OrderForm orderForm = new OrderForm();
    orderForm.setId(1000L);
    orderForm.setOrderSN(UUID.randomUUID().toString());

    Goods g1 = new Goods();
    g1.setId(1000L);
    g1.setGoodsName("苹果手机");

    Goods g2 = new Goods();
    g2.setId(2000L);
    g2.setGoodsName("华为手机");

    List<Goods> goodsList = new ArrayList<>();
    goodsList.add(g1);
    goodsList.add(g2);

    orderForm.setGoods(goodsList);

    OrderForm orderForm1 = new OrderForm();

    orderForms.add(orderForm);

    MockHttpServletRequestBuilder mockHttpServletRequestBuilder =
        MockMvcRequestBuilders.post("/postOrderForms")
        .content(JSONObject.toJSONString(orderForms))
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(mockHttpServletRequestBuilder);
    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_参数绑定_19

 

 

2)使用Map<String,Object>接收

@RequestMapping("/postMapOrderForm")
@ResponseBody
public Map<String,OrderForm> postMapOrderForm(@RequestBody Map<String,OrderForm> map){
    return map;
}
@Test
public void postMapOrderForm() throws Exception {
    Map<String,OrderForm> map = new HashMap<>();

    OrderForm orderForm = new OrderForm();
    orderForm.setId(1000L);
    orderForm.setOrderSN(UUID.randomUUID().toString());

    Goods g1 = new Goods();
    g1.setId(1000L);
    g1.setGoodsName("苹果手机");

    Goods g2 = new Goods();
    g2.setId(2000L);
    g2.setGoodsName("华为手机");

    List<Goods> goodsList = new ArrayList<>();
    goodsList.add(g1);
    goodsList.add(g2);

    orderForm.setGoods(goodsList);

    map.put("orderForm",orderForm);

    MockHttpServletRequestBuilder mockHttpServletRequestBuilder =
        MockMvcRequestBuilders.post("/postMapOrderForm")
        .content(JSONObject.toJSONString(map))
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON);

    ResultActions resultActions = mockMvc.perform(mockHttpServletRequestBuilder);
    resultActions.andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print());
}

new 对象手动交给spring boot 管理 springboot的model对象_spring boot_20