《Zuul網關》之GET\POST\PUT請求報文重組並支持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、POST\PUT請求(contentType !=“multipart/form-data”)

  POST\PUT請求需要通過重寫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、POST\PUT請求(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對請求報文的參數重置修改,由於不方便拷貝源代碼,采用純文本手敲代碼,難免會出現格式和魔數不規范,請讀者忽視。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM