在项目微服务的升级过程中,我们通常会设置一个网关,作为一个洪流的出入口,在Spring Cloud 中提供了对应的功能,也就是Spring Cloud Gateway。对于旧的项目springMVC,实际也就是将spring-webmvc升级为spring-webflux,但你会发现fromdata 形式的数据,在webmvc可以被封装成参数,而在webflux中却不能,是不支持吗?
在spring官方文档中,有提及对fromdata表单数据的获取,如下图
fromdata表单数据需要通过ServerWebExchange 的getFormData()获取【也可以通过getMultipartData()方法获取数据】
主要对数据处理的代码如下:
1 2 3 4 5 6 7 8 9 10 |
//exchange 为 ServerWebExchange exchange.getMultipartData().map(data ->{ Map<String, Part> stringPartMap = data.toSingleValueMap(); //表单参数名 Part name= stringPartMap.get("name"); //将name进行处理转换 处理转换代码 //返回处理后的值 return name; }); |
具体的例子:比如需要从表单中取出file(MultipartFile)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@PostMapping("/api/test2") public Object test2(ServerWebExchange exchange){ return exchange.getMultipartData().map(data ->{ Map<String, Part> stringPartMap = data.toSingleValueMap(); Part file = stringPartMap.get("file"); if(file instanceof FilePart){ File file1 = null; try { FilePart filePart = (FilePart)file; file1 = new File(filePart.filename()); filePart.transferTo(file1); //获取到MultipartFile MultipartFile multipartFile = FileUtil.fileToMultipartFile(file1); //业务代码块 } catch (Exception e) { throw new RuntimeException("参数分析错误!"); }finally { if(file1.exists()){ file1.delete(); } } } return "success"; }); } |
文件处理工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public class FileUtil { public static MultipartFile fileToMultipartFile(File file) { FileItem fileItem = createFileItem(file); return new CommonsMultipartFile(fileItem); } public static FileInputStream fileToFileInputStream(File file) { FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } return fileInputStream; } public static File filePartToFile(FilePart filePart,File file) { file = new File(filePart.filename()); filePart.transferTo(file); return file; } private static FileItem createFileItem(File file) { FileItemFactory factory = new DiskFileItemFactory(16, null); FileItem item = factory.createItem("textField", "text/plain", true, file.getName()); int bytesRead; byte[] buffer = new byte[8192]; try { FileInputStream fis = new FileInputStream(file); OutputStream os = item.getOutputStream(); while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); fis.close(); } catch (IOException e) { e.printStackTrace(); } return item; } } |
直此,我们就能够获取到fromdata的数据
但这样子对于想获取fromdata数据,每次都需要引用ServerWebExchange ,然后通过getMultipartData()方法获取数据,比较臃肿,能否对数据重新进行封装呢,答案是可以的,下面我通过注解和配置封装参数解析类 进行fromdata数据的二次封装,代码案例如下:
controller层代码:
1 2 3 4 5 6 7 8 9 |
@PostMapping("/api/test2") public String test2(@ParameterConversion(name = "file",type = MultipartFile.class) MultipartFile multipartFile, @ParameterConversion(name = "name",type = String.class) String name, @ParameterConversion(name = "id",type = Integer.class) Integer id){ //业务代码块 return "success"; } |
注解类
1 2 3 4 5 6 7 8 9 10 |
@Inherited @Documented @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface ParameterConversion { //解析的名称 String name(); //转化的类型 Class<?> type(); } |
WebConfigurer 配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Configuration public class WebConfigurer implements WebFluxConfigurer { /** * 加入自定义方法参数解析器 * @param configurer */ @Override public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { List<HttpMessageReader<?>> readers=new ArrayList<>(); //添加Http消息编解码器 readers.add(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder())); //消息编解码器与Resolver绑定 configurer.addCustomResolver(new ParamsResolver(readers)); } } |
请求参数二次解析封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
public class ParamsResolver extends AbstractMessageReaderArgumentResolver { public ParamsResolver(List<HttpMessageReader<?>> readers) { super(readers); } protected ParamsResolver(List<HttpMessageReader<?>> messageReaders, ReactiveAdapterRegistry adapterRegistry) { super(messageReaders, adapterRegistry); } /** * 判断是否需要解析参数 * @param methodParameter * @return */ @Override public boolean supportsParameter(MethodParameter methodParameter) { System.out.println("into supportsParameter"); if (!methodParameter.hasParameterAnnotation(ParameterConversion.class)) { return false; } else { ParameterConversion parameterAnnotation= methodParameter.getParameterAnnotation(ParameterConversion.class); return parameterAnnotation != null && methodParameter.getParameterType().isAssignableFrom(parameterAnnotation.type()) && Objects.nonNull(parameterAnnotation.name()); } } @Override public Mono<Object> resolveArgument(MethodParameter methodParameter, BindingContext bindingContext, ServerWebExchange serverWebExchange) { System.out.println("into resolveArgument"); ParameterConversion parameterAnnotation= methodParameter.getParameterAnnotation(ParameterConversion.class); String name = parameterAnnotation.name(); Class<?> type = parameterAnnotation.type(); return serverWebExchange.getMultipartData().map(data -> { Part part = data.toSingleValueMap().get(name); if (part == null) throw new RuntimeException("ParameterConversion fail"); Object partObj = partToClass(part, type); Assert.notNull(partObj,"ParameterConversion fail, 不支持该类型转换"); return partObj; }); } private Object partToClass(Part part,Class<?> type){ if (type == MultipartFile.class && part instanceof FilePart) { File file = null; try { FilePart filePart = (FilePart) part; file = new File(filePart.filename()); filePart.transferTo(file); return FileUtil.fileToMultipartFile(file); } finally { if (file != null && file.exists()) { file.delete(); } } } else if (type == FilePart.class) { return part; }else if (type == String.class){ return bufferToStr(part.content()); } else if (type == Integer.class) { return Integer.parseInt(bufferToStr(part.content())); } else if (type == BigDecimal.class) { return new BigDecimal(bufferToStr(part.content())); } else { return null; } } private String bufferToStr(Flux<DataBuffer> content){ AtomicReference<String> res = new AtomicReference<>(); content.subscribe(buffer -> { byte[] bytes = new byte[buffer.readableByteCount()]; buffer.read(bytes); DataBufferUtils.release(buffer); res.set(new String(bytes, StandardCharsets.UTF_8)); }); return res.get(); } } |
以上代码,为作者个人观点,如有不足处,请指教;
from:https://blog.csdn.net/qq_37044686/article/details/121176415