zoukankan      html  css  js  c++  java
  • 《Zuul网关》之GETPOSTPUT请求报文重组并支持multipart/form-data

    1、重组参数

      假设需要重新组装的参数如下:

    @Setter
    @Getter
    public class DecodeParameters implements Serializable{
    
      private static final long serialVersionUID = -874947393093003083830L;
    
      // 通用参数
      private String channelNo;
    
      //业务参数
      private String data;
    
    }

    2、GET请求

      GET请求主要通过RequestContext参数requestQueryParams重置,设定解密之后的参数数值来实现参数重组,转发给后端微服务。

    public void processRequestBody(DecodeParameters parameters){
    RequestContext context
    = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String method =request.getMethod().toUpperCase(); if(HttpMethod.GET.matches(method)){   Map<String,List<String>> requestQueryParam = context.getRequestQueryParams();     if(Objects.isNull(requestQueryParams)){    requestQueryParams = new HashMap<>();    }else{ requestQueryParams .remove("channelNo");    } //放置业务参数 if(StringUtils.isNotBlank(parameters.getData)){   JSONObject data = JSONObject.parseObject(paramters.getData());   for(String key : data.keySet()){    List<String> list = new ArrayList<String>(){     {       add(data.getString(key));     }   };   requestQueryParams.put(key,list);   } } //放置通用参数 List<String> list = new ArrayList<>(Arrays.asList(parameters.getChannelNo()));
    requestQueryParams.put (
    "channelNo", list);   }
    }

    3、POSTPUT请求(contentType !=“multipart/form-data”)

      POSTPUT请求需要通过重写RequestContext的HttpServletRequestWrapper InputStream流实现参数重组。

      contentType =“application/json”这类这类场景的POST或者PUT请求,主要是通过重新设定InputStream流实现请求参数的重组,转发给后端。

    public void processRequestBody(DecodeParameters parameters){
      RequestContext context = RequestContext.getCurrentContext();
      HttpServletRequest request = context.getRequest();
      String method =request.getMethod().toUpperCase();
      if(HttpMethod.POST.matches(method) || HttpMethod.PUT.matches(method)){
          JSONObject requestBodyObj= new JSONObject();
      requestBodyObj.put("channelNo", parameters.getChannelNo());
      //注意data先转化成json对象格式,后面同一转化成json字符串
      requestBodyObj.put("data", JSONObject.parseObject(parameters.getData()));
      String requestBody= JSONObject.toJSONString(requestBodyObj);
      byte[] reqBodyBytes =requestBody.getBytes();
      context.setRequest(new HttpServletRequestWrapper (context.getRequest()){
        @Override
        public ServletInputStream getInputStream() throws IOException{
          return new ServletInputStreamWrapper (reqBodyBytes);
        }
    
        @Override
        public int getContextLength(){
          return reqBodyBytes.length;
        }
    
        @Override
        public long getContentLengthLong(){
          return reqBodyBytes.length;
        });
        }
      }
    }
     

     

    4、POSTPUT请求(contentType =“multipart/form-data”) 

     contentType = “multipart/form-data”这类请求报文中,既有文本参数(data),又有文件参数(file),报文结构较为复杂。

    在POST和PUT请求中主要通过重构multipart/form-data request 实现参数的重组,转发给后端。

    public void processRequestBody(DecodeParameters param){
      RequestContext context = RequestContext.getCurrentContext();
      HttpServletRequest request = context.getRequest();
      String method =request.getMethod().toUpperCase();
      String contentType = request.getContentType();
      if(HttpMethod.POST.matches(method) || HttpMethod.PUT.matches(method)){
          if(StringUtils.isNotBlank(contextType) && contentType.toLowerCase().startsWith("multipart/form-data")){
          //重构multipart/form-data request
          MultipartEntityBuilder multiEntityBuilder = MultipartEntityBuilder.create()
          .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
          .setCharset(StandardCharsets.UTF_8)
          .setContentType(ContentType.create(multipart/form-data))
          //务必获取原始报文boudary数值并重新设置boudary
          .setBoundary(contentType.substring(contentType.indexOf("boundary+")+9))
    
          //数据包文本参数重组
          if(Objects.nonNull(param)){
          //规范业务参数格式
            String paramStr;
            if(StringUtils.isBlank(param.getData()) || !isJsonValidate(param.getData())){
              paramStr= JSONObject.toJSONString(param);
            }else{
              JSONObject dataObj = JSONObject.parseObject(param.getData());
              JSONObject paramObj = JSONObject.parseObject(JSONObject.toJSONString(param));
              paramObj.put("data", dataObj);
              paramStr = JSONObject.toJSONString(paramObj);
      }
    
      //重组
      ContentType dataContentType = ContentType.create("application/json",StandardCharsets.UTF_8);
      multiEntityBuilder.addTextBody("data",paramStr, dataContentType);
      //数据包文件参数重组
      MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
      MultipartHttpServletRequest multipartHttpServletRequest =resolver.resolveMultipart(request);
      boolean isSuccess = buildBinaryBody(multiEntityBuilder,multipartHttpServletRequest);
    if(!isSuccess){
        setRequestByPart(multipartHttpServletRequest, multiEntityBuilder);
      }   ByteArrayOutSteam byteArrayOutputStream();   multiEntityBuilder.build().writeTo(byteArrayOutputStream);   
    byte[] reqBodyBytes =byteArrayOutputSteam.toByteArray();   context.setRequest(new HttpServletRequestWrapper (context.getRequest()){     @Override     public ServletInputStream getInputStream() throws IOException{       return new ServletInputStreamWrapper (reqBodyBytes);     }     @Override     public int getContextLength(){       return reqBodyBytes.length;     }     @Override     public long getContentLengthLong(){       return reqBodyBytes.length;     });   } } private boolean isJsonValidate(String str){ try{   JSON.parse(str);   return true; }catch (JSONException e){   return false;   } } private boolean buildBinaryBody(MultipartEntityBuilder multiEntityBuilder, MultipartHttpServletRequest multipartHttpServletRequest)thows IOException{   MultiValueMap<String,MultipartFile> multiFiles = multipartHttpServletRequest.getMultiFileMap();   for(String key : multiFiles.keySet()){   List<MultipartFile> multipartFile = multiFiles.get(key);   for(MultipartFile multipartFile : multipartFiles){     String fileName = multipartFile.getOriginalFilename();     multiEntityBuilder.addBinaryBody(key, multipartFile.getInputStream(), ContentType.DEFAULT_BINARY,fileName);     }   }
      return multiFiles.isEmpty() ? false: true; }

    private void setRequestByPart(MultipartEntityBuilder multiEntityBuilder, MultipartHttpServletRequest multipartHttpServletRequest)
      thows IOException, ServletException{
      Collection<Part> parts = multipartHttpServletRequest.getParts();
      if(!CollectionUtils.isEmpty(parts)){
        for(Part part: parts){
            String name = part.getName();
            if(StringUtils.isNotBlank(name) && name.startsWith("file参数名")){
               ContentType contentType = ContentType.create(part.getContentType(), StandardCharsets.UTF_8);
               multiEntityBuilder.addBinaryBody(name, part.getInputStream(), contentType,part.getSubmittedFileName());
              }
          }
        }
    }

    后续

      本文主要为了讲述如何实现Zuul对请求报文的参数重置修改,由于不方便拷贝源代码,采用纯文本手敲代码,难免会出现格式和魔数不规范,请读者忽视。

  • 相关阅读:
    JAVA多线程实现和应用总结
    大话程序猿眼里的高并发
    重写Dijkstra
    再做一遍floyed
    优美的二分模型
    ACwing : 798. 差分矩阵
    ACwing_789. 数的范围
    快速排序超级强的模板
    1829:【02NOIP提高组】自由落体
    【02NOIP提高组】均分纸牌
  • 原文地址:https://www.cnblogs.com/gavincoder/p/14222970.html
Copyright © 2011-2022 走看看