Java实现一个解析CURL脚本小工具

该工具可以将CURL脚本中的Header解析为KV Map结构;获取URL路径、请求类型;解析URL参数列表;解析Body请求体:Form表单、Raw Body、KV Body、XML/JSON/TEXT结构体等。

使用示例

获取一个http curl脚本:

curl --location --request POST 'https://cainiao-inc.com?param_1=value_1&param_2=value_2' \
--header 'Cookie: USER_COOKIE' \
--header 'Content-Type: application/json' \
--data-raw '{"appName": "link","apiId": "TEST_API","content": {"address": "Cainiao Home","city": "Hangzhou"}
}'

执行解析例子:

实现原理

实现原理很简单:基于Java正则 + 责任链设计模式,按照Curl脚本的常见语法去匹配、解析即可~

按照Curl语法结构,可以将其拆分为 5 个部分:

  • URL路径:http://cainiao.com

  • URL参数列表:?param_1=valie_1&param_2=valie_2

  • 请求方法类型: 例如 POST、GET、DELETE、PUT...... 需要正则匹配-X --request等标识符

  • Header请求头:例如 Cookie、Token、Content-Type...... 需要正则匹配-H --header等标识符

  • Body请求体:可以分为form-data/-formdata-rawdata-urlencode-d--datakvbody等。格式可能包含JSON、XML、文本、KV键值对,二进制流(暂不支持解析)等等。

具体实现

流程简图:

类关系图:

CurlParserUtil

Curl解析工具类:

public class CurlParserUtil {/*** 该方法是用来解析CURL的入口。** @param curl 输入的CURL文本字符串* @return 返回解析后生成的CURL实体对象*/public static CurlEntity parse(String curl) {CurlEntity entity = CurlEntity.builder().build();ICurlHandler<CurlEntity, String> handlerChain = CurlHandlerChain.init();// 如需扩展其他解析器,继续往链表中add即可handlerChain.next(new UrlPathHandler()).next(new UrlParamsHandler()).next(new HttpMethodHandler()).next(new HeaderHandler()).next(new HttpBodyHandler());handlerChain.handle(entity, curl);return entity;}
}

CurlEntity

解析后得到的Curl实体类(这里分了5个部分)

@Data
@Builder
public class CurlEntity {/*** URL路径*/private String url;/*** 请求方法类型*/private Method method;/*** URL参数*/private Map<String, String> urlParams;/*** header参数*/private Map<String, String> headers;/*** 请求体*/private JSONObject body;public enum Method {GET,POST,PUT,DELETE}
}

ICurlHandler

责任链链表结构定义:

public interface ICurlHandler<R, S> {ICurlHandler<CurlEntity, String> next(ICurlHandler<CurlEntity, String> handler);void handle(CurlEntity entity, String curl);
}

CurlHandlerChain

责任链载体:

public abstract class CurlHandlerChain implements ICurlHandler<CurlEntity, String> {ICurlHandler<CurlEntity, String> next;@Overridepublic ICurlHandler<CurlEntity, String> next(ICurlHandler<CurlEntity, String> handler) {this.next = handler;return this.next;}@Overridepublic abstract void handle(CurlEntity entity, String curl);/*** for subclass call*/protected void nextHandle(CurlEntity curlEntity, String curl) {if (next != null) {next.handle(curlEntity, curl);}}protected void validate(String curl) {if (StringUtils.isBlank(curl)) {throw new IllegalArgumentException("Curl script is empty");}Matcher matcher = CURL_BASIC_STRUCTURE_PATTERN.matcher(curl);if (!matcher.find()) {throw new IllegalArgumentException("Curl script is invalid");}}public static CurlHandlerChain init() {return new CurlHandlerChain() {@Overridepublic void handle(CurlEntity entity, String curl) {this.validate(curl);// 替换掉可能存在的转译curl = curl.replace("\\", "");if (next != null) {next.handle(entity, curl);}}};}public void log(Object... logParams) {// Write log for subclass extensions}
}

UrlPathHandler

URL路径解析:

public class UrlPathHandler extends CurlHandlerChain {@Overridepublic void handle(CurlEntity entity, String curl) {String url = parseUrlPath(curl);entity.setUrl(url);this.log(url);super.nextHandle(entity, curl);}/*** 该方法用于解析URL路径。** @param curl 需要解析的URL,以字符串形式给出* @return URL中的路径部分。如果找不到,将返回null*/private String parseUrlPath(String curl) {Matcher matcher = CurlPatternConstants.URL_PATH_PATTERN.matcher(curl);if (matcher.find()) {return matcher.group(1) != null ? matcher.group(1) : matcher.group(3);}return null;}@Overridepublic void log(Object... logParams) {LogPrinter.info("UrlPathHandler execute: url={}", logParams);}
}

HttpMethodHandler

请求类型解析:

public class HttpMethodHandler extends CurlHandlerChain {@Overridepublic void handle(CurlEntity entity, String curl) {CurlEntity.Method method = parseMethod(curl);entity.setMethod(method);this.log(method);super.nextHandle(entity, curl);}private CurlEntity.Method parseMethod(String curl) {Matcher matcher = CurlPatternConstants.HTTP_METHOD_PATTERN.matcher(curl);Matcher defaultMatcher = CurlPatternConstants.DEFAULT_HTTP_METHOD_PATTERN.matcher(curl);if (matcher.find()) {String method = matcher.group(1);return CurlEntity.Method.valueOf(method.toUpperCase());} else if (defaultMatcher.find()) {// 如果命令中包含 -d 或 --data,没有明确请求方法,默认为 POSTreturn CurlEntity.Method.POST;} else {// 没有明确指定请求方法,默认为 GETreturn CurlEntity.Method.GET;}}@Overridepublic void log(Object... logParams) {LogPrinter.info("HttpMethodHandler execute: method={}", logParams);}
}

UrlParamsHandler

URL参数列表解析:

public class UrlParamsHandler extends CurlHandlerChain {@Overridepublic void handle(CurlEntity entity, String curl) {String url = extractUrl(curl);Map<String, String> urlParams = parseUrlParams(url);entity.setUrlParams(urlParams);this.log(urlParams);super.nextHandle(entity, curl);}private String extractUrl(String curl) {Matcher matcher = CurlPatternConstants.URL_PARAMS_PATTERN.matcher(curl);if (matcher.find()) {return matcher.group(1);}return null;}private Map<String, String> parseUrlParams(String url) {if (StringUtils.isBlank(url)) {return Collections.emptyMap();}Map<String, String> urlParams = new HashMap<>();// 提取URL的查询参数部分String[] urlParts = url.split("\\?");if (urlParts.length > 1) {// 只处理存在查询参数的情况String query = urlParts[1];// 解析查询参数到MapString[] pairs = query.split("&");for (String pair : pairs) {int idx = pair.indexOf("=");if (idx != -1 && idx < pair.length() - 1) {String key = pair.substring(0, idx);String value = pair.substring(idx + 1);urlParams.put(key, value);} else {// 存在无值的参数时urlParams.put(pair, null);}}}return urlParams;}@Overridepublic void log(Object... logParams) {LogPrinter.info("UrlParamsHandler execute: urlParams={}", logParams);}
}

HeaderHandler

Http Header解析:

public class HeaderHandler extends CurlHandlerChain{@Overridepublic void handle(CurlEntity entity, String curl) {Map<String, String> headers = parseHeaders(curl);entity.setHeaders(headers);this.log(headers);super.nextHandle(entity, curl);}private Map<String, String> parseHeaders(String curl) {if (StringUtils.isBlank(curl)) {return Collections.emptyMap();}Matcher matcher = CurlPatternConstants.CURL_HEADERS_PATTERN.matcher(curl);Map<String, String> headers = new HashMap<>();while (matcher.find()) {String header = matcher.group(1);String[] headerKeyValue = header.split(":", 2);if (headerKeyValue.length == 2) {// 去除键和值的首尾空白字符headers.put(headerKeyValue[0].trim(), headerKeyValue[1].trim());}}return headers;}@Overridepublic void log(Object... logParams) {LogPrinter.info("HeaderHandler execute: headers={}", logParams);}
}

HttpBodyHandler

Request Body请求体解析:

  • form-data/-form

  • data-urlencode

  • data-raw

  • default/-d/--data

格式可能包含JSON、XML、文本、KV键值对,二进制流(暂不支持解析)等等。

public class HttpBodyHandler extends CurlHandlerChain {@Overridepublic void handle(CurlEntity entity, String curl) {JSONObject body = parseBody(curl);entity.setBody(body);this.log(body);super.nextHandle(entity, curl);}private JSONObject parseBody(String curl) {Matcher formMatcher = CurlPatternConstants.HTTP_FROM_BODY_PATTERN.matcher(curl);if (formMatcher.find()) {return parseFormBody(formMatcher);}Matcher urlencodeMatcher = CurlPatternConstants.HTTP_URLENCODE_BODY_PATTERN.matcher(curl);if (urlencodeMatcher.find()) {return parseUrlEncodeBody(urlencodeMatcher);}Matcher rawMatcher = CurlPatternConstants.HTTP_ROW_BODY_PATTERN.matcher(curl);if (rawMatcher.find()) {return parseRowBody(rawMatcher);}Matcher defaultMatcher = CurlPatternConstants.DEFAULT_HTTP_BODY_PATTERN.matcher(curl);if (defaultMatcher.find()) {return parseDefaultBody(defaultMatcher);}return new JSONObject();}private JSONObject parseDefaultBody(Matcher defaultMatcher) {String bodyStr = "";if (defaultMatcher.group(1) != null) {// 单引号包裹的数据bodyStr = defaultMatcher.group(1);} else if (defaultMatcher.group(2) != null) {// 双引号包裹的数据bodyStr = defaultMatcher.group(2);} else {// 无引号的数据bodyStr = defaultMatcher.group(3);}// 特殊Case: username=test&password=secretMatcher kvMatcher = CurlPatternConstants.DEFAULT_HTTP_BODY_PATTERN_KV.matcher(bodyStr);if (kvMatcher.find()) {return parseKVBody(bodyStr);}return JSONObject.parseObject(bodyStr);}private JSONObject parseKVBody(String kvBodyStr) {JSONObject json = new JSONObject();String[] pairs = kvBodyStr.split("&");for (String pair : pairs) {int idx = pair.indexOf("=");String key = URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8);String value = URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8);json.put(key, value);}return json;}private JSONObject parseFormBody(Matcher formMatcher) {JSONObject formData = new JSONObject();// 重置指针匹配的位置formMatcher.reset();while (formMatcher.find()) {// 提取表单项String formItem = formMatcher.group(1) != null ? formMatcher.group(1) : formMatcher.group(2);// 分割键和值String[] keyValue = formItem.split("=", 2);if (keyValue.length == 2) {String key = keyValue[0];String value = keyValue[1];// 检测文件字段标记// PS: 理论上文件标记字段不需要支持if (value.startsWith("@")) {// 只提取文件名,不读取文件内容formData.put(key, value.substring(1));} else {// 放入表单数据formData.put(key, value);}}}return formData;}private JSONObject parseUrlEncodeBody(Matcher urlencodeMatcher) {JSONObject urlEncodeData = new JSONObject();// 重置指针匹配的位置urlencodeMatcher.reset();while (urlencodeMatcher.find()) {// 提取键值对字符串String keyValueEncoded = urlencodeMatcher.group(1);// 分隔键和值String[] keyValue = keyValueEncoded.split("=", 2);if (keyValue.length == 2) {String key = keyValue[0];String value = keyValue[1];// 对值进行URL解码String decodedValue = URLDecoder.decode(value, StandardCharsets.UTF_8);// 存入数据到JSON对象urlEncodeData.put(key, decodedValue);}}return urlEncodeData;}private JSONObject parseRowBody(Matcher rowMatcher) {String rawData = rowMatcher.group(1);if (isXML(rawData)) {// throw new IllegalArgumentException("Curl --data-raw content cant' be XML");return xml2json(rawData);}try {return JSON.parseObject(rawData);} catch (Exception e) {throw new IllegalArgumentException("Curl --data-raw content is not a valid JSON");}}public static boolean isXML(String xmlStr) {try {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();InputSource is = new InputSource(new StringReader(xmlStr));builder.parse(is);return true;} catch (Exception e) {return false;}}private JSONObject xml2json(String xmlStr) {try {org.json.JSONObject orgJsonObj = XML.toJSONObject(xmlStr);String jsonString = orgJsonObj.toString();return JSON.parseObject(jsonString);} catch (JSONException e) {throw new LinkConsoleException("Curl --data-raw content xml2json error", e);}}@Overridepublic void log(Object... logParams) {LogPrinter.info("HttpBodyHandler execute: body={}", logParams);}
}

CurlPatternConstants

正则匹配常量定义:

public interface CurlPatternConstants {/*** CURL基本结构校验*/Pattern CURL_BASIC_STRUCTURE_PATTERN = Pattern.compile("^curl (\\S+)");/*** URL路径匹配*/Pattern URL_PATH_PATTERN =Pattern.compile("(?:\\s|^)(?:'|\")?(https?://[^?\\s'\"]*)(?:\\?[^\\s'\"]*)?(?:'|\")?(?:\\s|$)");/*** 请求参数列表匹配*/Pattern URL_PARAMS_PATTERN = Pattern.compile("(?:\\s|^)(?:'|\")?(https?://[^\\s'\"]+)(?:'|\")?(?:\\s|$)");/*** HTTP请求方法匹配*/Pattern HTTP_METHOD_PATTERN = Pattern.compile("(?:-X|--request)\\s+(\\S+)");/*** 默认HTTP请求方法匹配*/Pattern DEFAULT_HTTP_METHOD_PATTERN = Pattern.compile(".*\\s(-d|--data|--data-binary)\\s.*");/*** 请求头匹配*/Pattern CURL_HEADERS_PATTERN = Pattern.compile("(?:-H|--header)\\s+'(.*?:.*?)'");/*** -d/--data 请求体匹配*/Pattern DEFAULT_HTTP_BODY_PATTERN = Pattern.compile("(?:--data|-d)\\s+(?:'([^']*)'|\"([^\"]*)\"|(\\S+))");Pattern DEFAULT_HTTP_BODY_PATTERN_KV = Pattern.compile("^([^=&]+=[^=&]+)(?:&[^=&]+=[^=&]+)*$");/*** --data-raw 请求体匹配*/Pattern HTTP_ROW_BODY_PATTERN = Pattern.compile("--data-raw '(.+?)'(?s)", Pattern.DOTALL);/*** --form 请求体匹配*/Pattern HTTP_FROM_BODY_PATTERN = Pattern.compile("--form\\s+'(.*?)'|-F\\s+'(.*?)'");/*** --data-urlencode 请求体匹配*/Pattern HTTP_URLENCODE_BODY_PATTERN = Pattern.compile("--data-urlencode\\s+'(.*?)'");}

有问题可以留言讨论,刷流量评论定期删除!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/353664.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Blazor 中基于角色的授权

介绍 Blazor用于使用 .NET 代码库创建交互式客户端 Web UI。Microsoft 默认在 Blazor 应用程序中提供了一个用于身份验证和授权的身份框架。请注意&#xff0c;他们目前使用 MVC Core Razor 页面作为身份验证 UI。使用“Microsoft.AspNetCore.Identity.UI”包库来实现这一点。…

学会python——制作一款天气查询工具(python实例七)

目录 1、认识Python 2、环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3、天气查询工具 3.1 代码构思 3.2 代码示例 3.3 运行结果 4、总结 1、认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的…

奇怪的缓存一致性问题

天猫国际用户Push中心承接了国际用户触达相关的需求&#xff0c;比如短信、端内消息投放等等&#xff0c;并存在较高的并发场景。 该系统此前发现了一个查询投放计划plan为null的异常情况&#xff0c;在初期排查时有些丈二和尚摸不着头脑&#xff0c;后面突然灵光乍现——原来是…

网络层 IP协议【计算机网络】【协议格式 || 分片 || 网段划分 || 子网掩码】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;前提 二&…

【Python】类和对象的深入解析

目录 前言 什么是类&#xff1f; 定义一个类 创建对象 访问和修改属性 方法 类的继承 多态 封装 特殊方法 属性装饰器 总结 前言 Python 是一种面向对象的编程语言&#xff0c;它允许程序员通过类和对象来组织和管理代码。面向对象编程&#xff08;OOP&#xff09…

被腰斩的颍川郡守赵广汉

在颍川&#xff0c;他发明了举报箱&#xff0c;铁腕扫黑除恶。因为曾经在郡府所在地阳翟&#xff08;禹州&#xff09;当过县令&#xff0c;熟悉颍川社情民意&#xff0c;所以&#xff0c;任职郡守后雷厉风行&#xff0c;才不到一年&#xff0c;不但制服了骄横的豪门大族&#…

1.0 Android中Activity的基础知识

一&#xff1a;Activity的定义 Activity是一个应用组件&#xff0c;它提供了一个用户界面&#xff0c;允许用户执行一个单一的、明确的操作&#xff0c;用户看的见的操作都是在activity中执行的。Activity的实现需要在manifest中进行定义&#xff0c;不让会造成程序报错。 1.…

关机充电动画:流程与定制

关机充电动画&#xff1a;流程与定制 基于MTK平台Android 11分析 生成logo.bin 关机充电动画是由一系列的bmp图片组成的&#xff0c;这些图片资源存在于vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo目录下&#xff08;当然不仅保护关机充电动画&#xff0c…

中医药人工智能大模型正式启动

6月15日&#xff0c;在横琴粤澳深度合作区举行的中医药广东省实验室&#xff08;以下简称横琴实验室&#xff09;第一届学术委员会第一次会议暨首届横琴中医药科技创新大会上&#xff0c;中医药横琴大模型、中药新药智能自动化融合创新平台同时启动。这也是该实验室揭牌半年来取…

Stable Diffusion WebUI 使用ControlNet:IP-Adapter保持生图的角色一致性

IP-Adapter-FaceID可以在保持人脸一致的条件下生成各种风格的图像。 下载 IP Adapter 需要的 Face ID 模型和 Lora 下载地址&#xff1a;https://huggingface.co/h94/IP-Adapter-FaceID/ 下载 ip-adapter-faceid-plusv2_sd15.bin 和 ip-adapter-faceid-plusv2_sd15_lora.saf…

瑞典农业科学大学《Nature Geoscience》(IF=18)!揭示北方森林碳汇对干旱的响应机制!

本文首发于“生态学者”微信公众号&#xff01; 北方森林覆盖了地球陆地面积的11%&#xff0c;储存了全球陆地碳储量的约三分之一。因此&#xff0c;它们被认为是减缓气候变化政策的一个重要因素。然而&#xff0c;环极寒带地区是气候变化速度最快的地区。这包括更频繁和更严重…

入门 Axure RP 9 | 原型设计基础教程

选择正确的原型设计工具并非易事&#xff0c;Axure RP 9能够快速完成原型设计。原型设计是一种经过时间考验的方法&#xff0c;可以将你的设计快速放置在用户的设备并交到他们手中。替代Axure RP 9的原型设计工具即时设计是一个完全集成的协同设计工具&#xff0c;无需使用不同…

计算机专业毕设-校园二手交易平台

1 项目介绍 基于SpringBoot的校园二手交易平台&#xff1a;前端Freemarker&#xff0c;后端 SpringBoot、Jpa&#xff0c;系统用户分为两类&#xff0c;管理员、学生&#xff0c;具体功能如下&#xff1a; 管理员&#xff1a; 基本功能&#xff1a;登录、修改个人信息、修改…

“AI引擎”澎湃动力!深圳机场携手实在智能打造民航财务数字员工

近日&#xff0c;深圳机场集团&#xff08;000089&#xff09;与实在智能达成合作&#xff0c;引入了业内领先的平台级自动化产品——实在RPA数字员工&#xff0c;部署实在智能集“自动化平台开箱即用解决方案咨询”为一体的数字员工解决方案&#xff0c;实现纳税申报、报表制作…

海思SS928/SD3403开发笔记1——使用串口调试开发板

该板子使用串口可以调试&#xff0c;下面是win11 调试 该板子步骤 1、给板子接入鼠标、键盘、usb转串口 2、下载SecureCRT&#xff0c;并科学使用 下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/11dIkZVstvHQUhE8uS1YO0Q 提取码&#xff1a;vinv 3、安装c…

Chromium 开发指南2024 Mac篇-安装和配置depot_tools工具(三)

1.引言 在前两篇指南中&#xff0c;我们详细介绍了在 macOS 环境下编译 Chromium 所需的硬件要求和系统依赖&#xff0c;并具体讲解了如何正确安装和配置 Xcode。通过这些步骤&#xff0c;您已经为编译 Chromium 打下了坚实的基础。然而&#xff0c;编译 Chromium 还需要配置一…

哪种考勤机好用,常见好用的考勤机种类

哪种考勤机好用&#xff0c;常见好用的考勤机种类 用考勤机完成上下班打卡制度&#xff0c;极大地为人事对公司的管理提供了便利。不同种类的考勤机均有各自的长处&#xff0c;那么究竟哪种考勤机比较好用呢&#xff1f;其中&#xff0c;智能云考勤机能够实现异地手机打卡&…

Window常用的脚本有哪些?快来看看有哪些是你正在用的!(欢迎评论补充~)

前言 在日常开发中&#xff0c;如果能熟练掌握以下这些使用频率很高的脚本&#xff0c;那工作起来真的是手拿把攥&#xff0c;事半功倍&#xff0c;接下来给大家介绍一些我们日常使用率很高的一些脚本&#xff01; 常用脚本(Batchfile & VBScript) 1.一键启动.bat 一次…

常见的网络设备

引入 园区网络安全部署场景 1、路由器&#xff1a; 跨网段通信设备 。 2、交换机&#xff1a; 同网段或跨网段通信设备。 3、AntiDDoS &#xff1a; DDoS 防御系统&#xff0c;通常旁挂部署于网络出口处&#xff0c; 位于防火墙上游&#xff0c;用于减轻防火墙报文处理负担。 …

C/S、B/S架构(详解)

一、CS、BS架构定义 CS架构&#xff08;Client-Server Architecture&#xff09;是一种分布式计算模型&#xff0c;其中客户端和服务器之间通过网络进行通信。在这种架构中&#xff0c;客户端负责向服务器发送请求&#xff0c;并接收服务器返回的响应。服务器则负责处理客户端的…