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

2025-02-08 16:50

本文主要是介绍使用Java实现一个解析CURL脚本小工具,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《使用Java实现一个解析CURL脚本小工具》文章介绍了如何使用Java实现一个解析CURL脚本的工具,该工具可以将CURL脚本中的Header解析为KVMap结构,获取URL路径、请求类型,解析UR...

版本

时间

修改内容

V1

2024.06.13

新建

V2

2024.06.28

更新body和请求类型筛选的正则表达式内容,特殊换符和转移符剔除

该工具可以将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脚本小工具

实现原理

实现原理很简单:基于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、Tokenandroid、Content-Type...... 需要正则匹配-H --header等标识符
  • Body请求体:可以分为form-data/-formdata-rawdata-urlencode-d--datakvbody等。格式可能包含JSON、XML、文本、KV键值对,二进制流(暂不支持解析)等等。

具体实现

流程简图:

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

类关系图:

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

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;
 
    @Override
    public ICurlHandler<CurlEntity, String> next(ICurlHandler<CujsrlEntity, String> handler) {
        this.next = handler;
        return this.next;
    }
 
    @Override
    public 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() {
            @Override
            public void handle(CurlEntity entity, String curl) {
                this.validate(curl);
 
                // 替换掉可能存在的转译(字符串中的空白字符,包括空格、换行符和制表符...)
                curl = curl.replace("\\", "")
                        .replace("\n", "")
                        .replace("\t", "");
 
                if (next != null) {
                    next.handle(entity, curl);
                }
            }
        };
    }
 
    public void log(Object... logParams) {
        // Write log for subclass extensions
    }
}

UrlPathHandler

URL路径解析:

public class UrlPathHandler extends CurlHandlerChain {
 
    @Override
    public 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;
    }
 
    @Override
    public void log(Object... logParams) {
        LogPrinter.info("UrlPathHandler execute: url={}", logParams);
    }
}

HttpMethodHandler

请求类型解析:

public class HttpMethodHandler extends CurlHandlerChain {
 
    @Override
    public 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,没有明确请求方法,默认为 POST
            return CurlEntity.Method.POST;
        } else {
            // 没有明确指定请求方法,默认为 GET
            return CurlEntity.Method.GET;
        }
    }
 
    @Override
    public void log(Object... logParams) {
        LogPrinter.info("HttpMethodHandler execute: method={}", logParams);
    }
}

UrlParamsHandler

URL参数列表解析:

public class UrlParamsHandler extends CurlHandlerChain {
 
    @Override
    public 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) {
   PlhfvFuX         // 只处理存在查询参数的情况
            String query = urlParts[1];
            // 解析查询参数到Map
            String[] 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;
    }
 
    @Override
    public void log(Object... logParams) {
        LogPrinter.info("UrlParamsHandler execute: urlParams={}", logParams);
    }
}

HeaderHandler

Http Header解析:

public class HeaderHandler extends CurlHandlerChain{
    
    @Override
    public 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;
    }
 
    @Override
    public 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 {
    @Override
    public 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);
        }
 
        // 判断是否是json结构
        if (isJSON(bodyStr)) {
            return JSONObject.parseobject(bodyStr);
        }
 
        // 特殊Case: username=test&password=secret
        Matcher kvMatcher = CurlPatternConstants.DEFAULT_HTTP_BODY_PATTERN_KV.matcher(bodyStr);
        return kvMatcher.matches() ? parseKVBody(bodyStr) : new JSONObject();
    }
 
    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)php;
 
            // 分隔键和值
            String[] keyValue = keyValueEncoded.split("=", 2);
            if (keyValue.http://www.chinasem.cnlength == 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");
        }
    }
 
    private boolean isJSON(String jsonStr) {
        try {
            JSONObject.parseObject(jsonStr);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
 
    public static boolean isXML(String xmlStr) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setFeature(SecurityConstants.DDD, true);
            factory.setFeature(SecurityConstants.EGE, false);
            factory.setFeature(SecurityConstants.EPE, false);
 
            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);
        }
    }
 
    @Override
    public 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+)");
    Pattern HTTP_METHOD_PATTERN = Pattern.compile("curl\\s+[^\\s]*\\s+(?:-X|--request)\\s+'?(GET|POST)'?");
 
    /**
     * 默认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.DOTALL);
    Pattern DEFAULT_HTTP_BODY_PATTERN_KV = Pattern.compile("^([^=&]+=[^=&]+)(?:&[^=&]+=[^=&]+)*$", Pattern.DOTALL);
 
    /**
     * --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+'(.*?)'");
 
}

其他代码

public class SecurityConstants {
    public static String DDD = "http://apache.org/xml/features/disallow-doctype-decl";
    public static String EGE = "http://xml.org/sax/features/external-general-entities";
    public static String EPE = "http://xml.org/sax/features/external-parameter-entities";
    public static String LED = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
}

以上就是使用Java实现一个解析CURL脚本小工具的详细内容,更多关于Java实现解析CURL工具的资料请关注China编程(www.chinasem.cn)其它相关文章!

这篇关于使用Java实现一个解析CURL脚本小工具的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153347

相关文章

使用Python实现PDF与SVG互转

《使用Python实现PDF与SVG互转》SVG(可缩放矢量图形)和PDF(便携式文档格式)是两种常见且广泛使用的文件格式,本文将详细介绍如何使用Python实现SVG和PDF之间的相互转... 目录使用工具使用python将SVG转换为PDF使用Python将SVG添加到现有PDF中使用Python将PD

使用Python实现图片和base64转换工具

《使用Python实现图片和base64转换工具》这篇文章主要为大家详细介绍了如何使用Python中的base64模块编写一个工具,可以实现图片和Base64编码之间的转换,感兴趣的小伙伴可以了解下... 简介使用python的base64模块来实现图片和Base64编码之间的转换。可以将图片转换为Bas

如何利用Python实现给Excel表格截图

《如何利用Python实现给Excel表格截图》这篇文章主要为大家详细介绍了如何利用Python实现给Excel表格截图功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 我搜索了网络上的方案,感觉把 Excel 表格转换为 html 再用 platwright 截图是比China编程较顺

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

异步线程traceId如何实现传递

《异步线程traceId如何实现传递》文章介绍了如何在异步请求中传递traceId,通过重写ThreadPoolTaskExecutor的方法和实现TaskDecorator接口来增强线程池,确保异步... 目录前言重写ThreadPoolTaskExecutor中方法线程池增强总结前言在日常问题排查中,

css实现图片旋转功能

《css实现图片旋转功能》:本文主要介绍了四种CSS变换效果:图片旋转90度、水平翻转、垂直翻转,并附带了相应的代码示例,详细内容请阅读本文,希望能对你有所帮助... 一 css实现图片旋转90度.icon{ -moz-transform:rotate(-90deg); -webkit-transfo

Linux编译器--gcc/g++使用方式

《Linux编译器--gcc/g++使用方式》文章主要介绍了C/C++程序的编译过程,包括预编译、编译、汇编和链接四个阶段,并详细解释了每个阶段的作用和具体操作,同时,还介绍了调试和发布版本的概念... 目录一、预编译指令1.1预处理功能1.2指令1.3问题扩展二、编译(生成汇编)三、汇编(生成二进制机器语

redis防止短信恶意调用的实现

《redis防止短信恶意调用的实现》本文主要介绍了在场景登录或注册接口中使用短信验证码时遇到的恶意调用问题,并通过使用Redis分布式锁来解决,具有一定的参考价值,感兴趣的可以了解一下... 目录1.场景2.排查3.解决方案3.1 Redis锁实现3.2 方法调用1.场景登录或注册接口中,使用短信验证码场

10个Python Excel自动化脚本分享

《10个PythonExcel自动化脚本分享》在数据处理和分析的过程中,Excel文件是我们日常工作中常见的格式,本文将分享10个实用的Excel自动化脚本,希望可以帮助大家更轻松地掌握这些技能... 目录1. Excel单元格批量填充2. 设置行高与列宽3. 根据条件删除行4. 创建新的Excel工作表5