# demo-web **Repository Path**: cugjack/demo-web ## Basic Information - **Project Name**: demo-web - **Description**: Java中的SpringWebMVC的demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-06-26 - **Last Updated**: 2023-10-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring Web MVC ## Springboot MVC 接收浏览器发送的请求 ## GET get请求的参数写法有两种,一种是直接一个一个写出get后面的参数,第二种是创建一个类,将参数名作为字段名,这两种都可以正常获取参数 ``` @GetMapping("/onlyGet") public Integer getPerson(Integer id, String name, @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) { log.info("id:{}, name:{}, birthday:{}", id, name, birthday); return 0; } ``` ``` @GetMapping("/classGet") public Integer classGetPerson(Person person) { try { log.info(objectMapper.writeValueAsString(person)); } catch (JsonProcessingException e) { e.printStackTrace(); } return 0; } ``` ### Date参数 请求参数中含有日期类时,需要进行特殊处理,否则会报错 ```localhost:10014/person/onlyGet?id=1&name=张三&birthday=2021-03-12``` > 2023-05-22 16:31:02.107 WARN 48452 --- [io-10014-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value [20210312]; nested exception is java.lang.IllegalArgumentException] 对于GET请求,可以直接在请求参数上添加注解`@DateTimeFormat`,然后定义类型,如下: ``` @GetMapping("/onlyGet") public Integer getPerson(Integer id, String name, @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) { log.info("id:{}, name:{}, birthday:{}", id, name, birthday); return 0; } ``` 对于post请求,也可以直接在参数上添加注解`@DateTimeFormat`,是可以生效的 ``` @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthday; ``` 注:`@DateTimeFormat`注解仅限于`Date`类,其他的时间类不支持此注解 ### LocalDateTime参数 对于Post请求中的类,使用`LocalDateTime`的时候,需要自定义序列化和反序列化类,如下: ```java public class LocalDateTimeDeserializer extends JsonDeserializer { DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Override public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { String text = jsonParser.getText(); LocalDateTime localDateTime = LocalDate.parse(text, dateTimeFormatter).atStartOfDay(); return localDateTime; } } ``` ```java public class MyLocalDateTimeSerializer extends StdSerializer { DateTimeFormatter dfDate = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public MyLocalDateTimeSerializer() { this(null); } public MyLocalDateTimeSerializer(Class t) { super(t); } @Override public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(dfDate.format(localDateTime)); } } ``` 但是可能会出现下面的异常 > com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.example.demoweb.entity.Person["updateTime"]) 这是因为Java8之后,jackson不再支持序列化LocalDateTime,所以需要自定义序列化 ### 枚举类 前端传过来的 ## POST post比较复杂,因为前端请求时,contentType多种多样,有表单的,也有请求体的,所以需要分情况讨论 ### 1. 表单 如果不加任何注解,就仅仅是在请求参数中给一个类,那么就相当于直接在url后拼接参数 `localhost:10014/person/onlyPost?id=1&name=张三` ``` @PostMapping("/onlyPost") public Integer postPerson(Person person) { try { log.info(objectMapper.writeValueAsString(person)); } catch (JsonProcessingException e) { e.printStackTrace(); } return 0; } ``` 在postman中,将参数放在url后面和放在formdata都是可以的 ![](./images/QQ截图20230522165345.png) 但是一般不建议这么做,还是都放在formdata中比较合适 ### 2.请求体 对于使用JSON封装了的请求来说,即contentType=application/json,必须要在请求体上添加注解`@RequestBody` ``` @PostMapping("/bodyPost") public Integer bodyPostPerson(@RequestBody Person person) { try { log.info(objectMapper.writeValueAsString(person)); } catch (JsonProcessingException e) { e.printStackTrace(); } return 0; } ``` 这种情况下,如果要处理Date类,就需要在类的字段上添加注解 ```java @Data public class Person { private Integer id; private String name; @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthday; } ``` 这种情况下,只要在字段上添加了注解`@DateTimeFormat`,就可以实现接收时间参数Date ### 枚举类 枚举类也分为GET和POST get请求的时候,可以直接写枚举类,然后传参的时候,直接传对应的名称,如果传错了,是会报错的 而且,不管枚举类是否含有参数,或者是没有参数,都可以直接传枚举类的值 `localhost:10014/person/enumGet?id=1&timeType=DAT` `localhost:10014/person/enumParamGet?id=1&gender=MALE` > 2023-05-22 17:22:51.232 WARN 33636 --- [io-10014-exec-5] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.example.demoweb.enums.TimeType'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [com.example.demoweb.enums.TimeType] for value [DAt]; nested exception is java.lang.IllegalArgumentException: No enum constant com.example.demoweb.enums.TimeType.DAt] 在post请求中,需要对类中的枚举类添加注解 ``` @JsonDeserialize(using = TimeTypeDeserializer.class) private TimeType timeType; ``` 然后自行编写反序列化程序 ```java public class TimeTypeDeserializer extends JsonDeserializer { @Override public TimeType deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { String text = jsonParser.getText(); return TimeType.valueOf(text); } } ``` 这样,就可以在前端直接输入对应的枚举类的值 ### 3.上传文件 #### 简单的上传文件 ``` @ApiOperation("上传单个文件") @PostMapping("/upload/single") public String uploadFile(MultipartFile file) { if (file == null) { return "上传文件为空"; } return saveFile(file); } private String saveFile(MultipartFile file) { //1. 获取原始文件名 String originalFilename = file.getOriginalFilename(); log.info("文件名:{}", originalFilename); // 获取文件后缀 String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); //2. 使用UUID重新生成文件名,防止文件名称重复造成文件覆盖 String fileName = UUID.randomUUID() + suffix; //3. 创建一个目录对象 File dir = new File(basedir); //4. 判断当前目录是否存在 if (!dir.exists()) { //若目录不存在,需要创建 dir.mkdirs(); } //5. 将临时文件转存到指定位置 try { file.transferTo(new File(basedir + File.separator + fileName)); } catch (IOException e) { e.printStackTrace(); } return fileName; } ``` #### 携带参数的上传文件 参考文档:https://www.cnblogs.com/handsomehui/p/15691768.html 文件和@RequestBody是不可能同时存在的,要么使用表单,要么使用字符串,然后解析成对象