Skip to content

RenderAPI 是一个强大的 Go 语言 HTTP 客户端库,专为模板驱动的 API 请求设计。它允许用户通过 JSON 模板定义 HTTP 请求,支持动态数据插入和请求转换。

Notifications You must be signed in to change notification settings

birdmichael/RenderAPI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RenderAPI

最新更新

RenderAPI 现已实现以下重要更新:

  1. 钩子系统重构:将钩子实现分离到独立文件中,提高可维护性

    • custom_hook.go - 包含自定义钩子实现
    • js_hook.go - JavaScript钩子实现
    • cmd_hook.go - 命令行钩子实现
  2. 异步钩子支持:所有钩子现在都支持同步和异步执行模式

  3. 前置和后置钩子分离:模板定义中钩子分为beforeHooksafterHooks两部分

  4. 改进的缓存系统:支持配置TTL和自定义缓存键模式

  5. 重试机制增强:完善的重试策略,支持指数退避


RenderAPI 是一个强大的 Go 语言 HTTP 客户端库,专为模板驱动的 API 请求设计。它允许用户通过 JSON 模板定义 HTTP 请求,支持动态数据插入和请求转换。

主要特性

  • 灵活的 HTTP 客户端,支持所有标准 HTTP 方法
  • 强大的 JSON 模板引擎,支持动态数据注入
  • 可扩展的钩子系统,用于请求/响应拦截和修改
  • 内置丰富的模板函数库,增强模板处理能力
  • 缓存机制提高性能
  • 简单直观的 API 设计

安装

go get github.com/birdmichael/RenderAPI

基本用法

package main

import (
	"fmt"
	"github.com/birdmichael/RenderAPI/pkg/client"
	"github.com/birdmichael/RenderAPI/pkg/hooks"
)

func main() {
	// 创建 HTTP 客户端
	c := client.NewClient("https://api.example.com", 10)
	
	// 添加认证令牌
	c.SetHeader("Authorization", "Bearer your-token")
	
	// 添加请求日志钩子
	c.AddBeforeRequestHook(hooks.LoggingHook)
	
	// 发送 GET 请求
	resp, err := c.Get("/users")
	if err != nil {
		fmt.Printf("请求失败: %v\n", err)
		return
	}
	
	// 读取响应
	body, err := client.ReadResponseBody(resp)
	if err != nil {
		fmt.Printf("读取响应失败: %v\n", err)
		return
	}
	
	fmt.Println(string(body))
}

使用 JSON 模板

package main

import (
	"fmt"
	"github.com/birdmichael/RenderAPI/pkg/client"
)

func main() {
	c := client.NewClient("https://api.example.com", 10)
	
	// 定义 JSON 模板
	template := `{
		"user": {
			"name": "{{.name}}",
			"age": {{.age}},
			"email": "{{.email}}"
		}
	}`
	
	// 准备数据
	data := map[string]interface{}{
		"name":  "张三",
		"age":   30,
		"email": "zhangsan@example.com",
	}
	
	// 发送带有模板的 POST 请求
	resp, err := c.PostWithTemplate("/users", template, data)
	if err != nil {
		fmt.Printf("请求失败: %v\n", err)
		return
	}
	
	// 处理响应...
}

使用文件模板和数据

package main

import (
	"fmt"
	"github.com/birdmichael/RenderAPI/pkg/client"
)

func main() {
	c := client.NewClient("https://api.example.com", 10)
	
	// 使用文件模板和数据文件发送请求
	resp, err := c.PostWithTemplateFile("/users", "templates/user.json", "data/user_data.json")
	if err != nil {
		fmt.Printf("请求失败: %v\n", err)
		return
	}
	
	// 处理响应...
}

内置模板函数

RenderAPI 的模板引擎内置了丰富的函数库,使模板操作更加灵活强大。以下是可用的内置函数分类:

字符串操作函数

函数名 说明 示例
toUpper 转换为大写 {{ toUpper "hello" }} => "HELLO"
toLower 转换为小写 {{ toLower "HELLO" }} => "hello"
title 首字母大写 {{ title "hello world" }} => "Hello World"
trim 去除前后空格 {{ trim " hello " }} => "hello"
trimPrefix 去除前缀 {{ trimPrefix "hello world" "hello " }} => "world"
trimSuffix 去除后缀 {{ trimSuffix "hello world" " world" }} => "hello"
replace 替换字符串(有次数限制) {{ replace "hello" "l" "L" 1 }} => "heLlo"
replaceAll 替换全部 {{ replaceAll "hello" "l" "L" }} => "heLLo"
split 分割字符串 {{ split "a,b,c" "," }} => ["a", "b", "c"]
join 连接字符串 {{ join (split "a,b,c" ",") "-" }} => "a-b-c"
contains 是否包含子串 {{ contains "hello" "ll" }} => true
hasPrefix 是否有前缀 {{ hasPrefix "hello" "he" }} => true
hasSuffix 是否有后缀 {{ hasSuffix "hello" "lo" }} => true
length 字符串长度 {{ length "hello" }} => 5
regexMatch 正则匹配 {{ regexMatch "^h.*o$" "hello" }} => true
regexReplace 正则替换 {{ regexReplace "[aeiou]" "*" "hello" }} => "h*ll*"
urlEncode URL编码 {{ urlEncode "hello world" }} => "hello+world"
urlDecode URL解码 {{ urlDecode "hello+world" }} => "hello world"
htmlEscape HTML转义 {{ htmlEscape "<div>" }} => "&lt;div&gt;"
htmlUnescape HTML反转义 {{ htmlUnescape "&lt;div&gt;" }} => "<div>"
substr 子字符串 {{ substr "hello" 1 2 }} => "el"
repeat 重复字符串 {{ repeat "ab" 3 }} => "ababab"

日期时间函数

函数名 说明 示例
now 当前时间 {{ now }} => 当前时间对象
formatTime 格式化时间 {{ formatTime (now) "2006-01-02" }} => 如 "2023-01-01"
formatDate 格式化日期 {{ formatDate (now) }} => 如 "2023-01-01"
formatDateTime 格式化日期时间 {{ formatDateTime (now) }} => 如 "2023-01-01 12:34:56"
parseTime 解析时间字符串 {{ parseTime "2006-01-02" "2023-01-01" }} => 时间对象
addDate 添加天数 {{ addDate (now) 7 }} => 一周后的时间
addHours 添加小时 {{ addHours (now) 2 }} => 两小时后的时间
addMinutes 添加分钟 {{ addMinutes (now) 30 }} => 30分钟后的时间
since 计算时间差(自某时刻起) {{ since (parseTime "2006-01-02" "2023-01-01") }} => 时间差
until 计算时间差(距某时刻) {{ until (parseTime "2006-01-02" "2023-01-01") }} => 时间差
isAfter 是否在之后 {{ isAfter (now) (parseTime "2006-01-02" "2000-01-01") }} => true
isBefore 是否在之前 {{ isBefore (now) (parseTime "2006-01-02" "2030-01-01") }} => true
year 获取年份 {{ year (now) }} => 如 2023
month 获取月份 {{ month (now) }} => 如 "January"
day 获取日 {{ day (now) }} => 如 1
weekday 获取星期几 {{ weekday (now) }} => 如 "Monday"
unixTime 获取Unix时间戳 {{ unixTime (now) }} => 如 1672531200
fromUnixTime 从Unix时间戳创建时间 {{ fromUnixTime 1672531200 }} => 对应的时间对象

数学运算函数

函数名 说明 示例
add 加法 {{ add 1 2 }} => 3
sub 减法 {{ sub 5 2 }} => 3
mul 乘法 {{ mul 2 3 }} => 6
div 除法 {{ div 6 2 }} => 3
mod 取模 {{ mod 7 3 }} => 1
ceil 向上取整 {{ ceil 3.2 }} => 4
floor 向下取整 {{ floor 3.8 }} => 3
round 四舍五入 {{ round 3.5 }} => 4
max 最大值 {{ max 1 5 }} => 5
min 最小值 {{ min 1 5 }} => 1
abs 绝对值 {{ abs -5 }} => 5
pow 幂运算 {{ pow 2 3 }} => 8
sqrt 平方根 {{ sqrt 16 }} => 4
rand 随机数(0-1) {{ rand }} => 随机小数
randInt 随机整数 {{ randInt 1 10 }} => 1到10间的随机整数

数据转换函数

函数名 说明 示例
toString 转换为字符串 {{ toString 123 }} => "123"
toInt 转换为整数 {{ toInt "123" }} => 123
toFloat 转换为浮点数 {{ toFloat "3.14" }} => 3.14
toBool 转换为布尔值 {{ toBool "true" }} => true
jsonEncode JSON编码 {{ jsonEncode (dict "name" "张三") }} => {"name":"张三"}
jsonDecode JSON解码 {{ (jsonDecode "{\"name\":\"张三\"}").name }} => "张三"
prettifyJSON 美化JSON {{ prettifyJSON "{\"name\":\"张三\"}" }} => 格式化后的JSON

集合操作函数

函数名 说明 示例
first 获取第一个元素 {{ first .items }} => 集合第一个元素
last 获取最后一个元素 {{ last .items }} => 集合最后一个元素
slice 切片 {{ slice .items 1 3 }} => 索引1到3的元素
append 追加元素 {{ append .items "new" }} => 添加元素后的集合
indexOf 查找索引 {{ indexOf .items "item" }} => 元素在集合中的索引
reverse 反转集合 {{ reverse .items }} => 反转后的集合
keys 获取Map的键 {{ keys .dict }} => 所有键的切片
values 获取Map的值 {{ values .dict }} => 所有值的切片
hasKey 是否有键 {{ hasKey .dict "name" }} => 是否包含指定键
sum 求和 {{ sum .numbers }} => 数组所有元素的和
avg 求平均值 {{ avg .numbers }} => 数组元素的平均值

条件逻辑函数

函数名 说明 示例
ternary 三元操作符 {{ ternary true "真" "假" }} => "真"
defaultValue 默认值 {{ defaultValue .name "默认名称" }} => 当name为nil时返回默认值
coalesce 返回第一个非空值 {{ coalesce .name .nickname "匿名" }} => 第一个非nil非空字符串值
and 逻辑与 {{ and true false }} => false
or 逻辑或 {{ or true false }} => true
not 逻辑非 {{ not true }} => false
eq 相等 {{ eq 5 5 }} => true
ne 不等 {{ ne 5 6 }} => true
lt 小于 {{ lt 5 10 }} => true
le 小于等于 {{ le 5 5 }} => true
gt 大于 {{ gt 10 5 }} => true
ge 大于等于 {{ ge 5 5 }} => true

加密与编码函数

函数名 说明 示例
md5 MD5哈希 {{ md5 "hello" }} => MD5哈希字符串
sha1 SHA1哈希 {{ sha1 "hello" }} => SHA1哈希字符串
sha256 SHA256哈希 {{ sha256 "hello" }} => SHA256哈希字符串
base64Encode Base64编码 {{ base64Encode "hello" }} => "aGVsbG8="
base64Decode Base64解码 {{ base64Decode "aGVsbG8=" }} => "hello"
hexEncode 十六进制编码 {{ hexEncode "hello" }} => "68656c6c6f"
hexDecode 十六进制解码 {{ hexDecode "68656c6c6f" }} => "hello"

模板示例

以下是使用内置函数的模板示例:

基本数据处理

{
  "user": {
    "name": "{{ toUpper .name }}",
    "email": "{{ toLower .email }}",
    "registered_at": "{{ formatDateTime (now) }}",
    "is_active": {{ toBool .status }},
    "subscription": {
      "type": "{{ defaultValue .plan "free" }}",
      "expires_in_days": {{ div (toFloat .expires_seconds) 86400 }}
    }
  },
  "security": {
    "token_hash": "{{ sha256 .token }}",
    "login_ip": "{{ regexReplace "(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)" "$1.$2.XXX.XXX" .ip }}"
  }
}

条件逻辑

{
  "order": {
    "id": "{{ .order_id }}",
    "status": "{{ .status }}",
    "total": {{ .total }},
    "discount": {{ ternary (gt (toFloat .total) 1000) 0.15 0.05 }},
    "final_price": {{ sub (toFloat .total) (mul (toFloat .total) (ternary (gt (toFloat .total) 1000) 0.15 0.05)) }},
    "shipping": {
      "method": "{{ coalesce .shipping_method .default_shipping "standard" }}",
      "estimate_days": {{ ternary (eq .shipping_method "express") 1 (ternary (eq .shipping_method "priority") 3 7) }}
    }
  }
}

集合处理

{
  "analytics": {
    "total_items": {{ length .items }},
    "categories": {{ keys .categories }},
    "most_expensive": {{ last (slice (sort .prices) 0 (len .prices)) }},
    "average_price": {{ avg .prices }},
    "price_summary": {
      "min": {{ min (index .prices 0) (index .prices 1) }},
      "max": {{ max (index .prices 0) (index .prices 1) }}
    }
  },
  "highlighted_item": {{ jsonEncode (first .featured_items) }}
}

钩子系统

RenderAPI提供了强大的钩子系统,支持前置钩子(BeforeHooks)和后置钩子(AfterHooks),允许你在请求前后进行拦截和修改。现在钩子系统还支持异步执行:

// 添加请求日志钩子
client.AddBeforeHook(&hooks.LoggingHook{})

// 添加认证钩子
client.AddBeforeHook(hooks.NewAuthHook("your-token"))

// 添加响应日志钩子
client.AddAfterHook(&hooks.ResponseLogHook{})

// 添加字段转换钩子
transformMap := map[string]string{
    "user": "phone"  // 将 user 字段转换为 phone 字段
}
client.AddBeforeHook(hooks.NewFieldTransformHook(transformMap))

// 添加自定义钩子
client.AddBeforeHook(&hooks.CustomFunctionHook{
    BeforeFn: func(req *http.Request) (*http.Request, error) {
        req.Header.Set("X-Custom-Header", "value")
        return req, nil
    },
})

JavaScript脚本钩子

你可以使用JavaScript脚本来动态修改请求和响应:

// 从文件加载JavaScript脚本钩子
if err := client.AddJSHookFromFile("scripts/transform_request.js", false, 30); err != nil {
    log.Fatalf("加载脚本失败: %v", err)
}

// 从字符串加载JavaScript脚本钩子
scriptContent := `
function processRequest(request) {
    // 修改请求体
    var body = JSON.parse(request.body);
    body.timestamp = new Date().toISOString();
    request.body = JSON.stringify(body);
    return request;
}
`
if err := client.AddJSHookFromString(scriptContent, false, 30); err != nil {
    log.Fatalf("添加脚本钩子失败: %v", err)
}

JavaScript脚本示例:

// scripts/transform_request.js
function processRequest(request) {
    // 读取请求体
    var body = JSON.parse(request.body);
    
    // 修改请求体
    if (body.user) {
        body.user.name = body.user.name.toUpperCase();
        body.user.created_at = new Date().toISOString();
        
        // 使用内置的RSA加密函数加密敏感信息
        if (body.user.password) {
            body.user.password = rsaEncryptGo(body.user.password, publicKey);
        }
    }
    
    // 返回修改后的请求体
    request.body = JSON.stringify(body);
    return request;
}

命令行钩子

你可以使用命令行脚本处理请求和响应:

// 添加命令行钩子(非异步,30秒超时)
client.AddCommandHook("jq '.user.name = .user.name | ascii_upcase'", false, 30)

模板定义中的钩子

在模板定义文件中,你可以指定前置钩子和后置钩子:

{
  "request": {
    "method": "POST",
    "baseURL": "https://api.example.com",
    "path": "/users",
    "headers": {
      "Content-Type": "application/json"
    }
  },
  "beforeHooks": [
    {
      "type": "js",
      "name": "authHook",
      "script": "function processRequest(request) { console.log('Auth处理请求...'); return request; }",
      "async": false,
      "timeout": 10
    },
    {
      "type": "command",
      "name": "timestamp",
      "command": "jq '.body.timestamp = now'",
      "async": false,
      "timeout": 3
    }
  ],
  "afterHooks": [
    {
      "type": "js",
      "name": "postProcess",
      "script": "function processResponse(response) { console.log('后置处理响应...'); return response; }",
      "async": false,
      "timeout": 5
    }
  ],
  "body": {
    "user": {
      "name": "{{.name}}",
      "email": "{{.email}}"
    }
  }
}

缓存系统

RenderAPI 提供了内置的缓存系统,可以提高性能并减少重复请求。在模板定义中配置缓存:

{
  "request": {
    "method": "GET",
    "path": "/users/{{.user_id}}"
  },
  "caching": {
    "enabled": true,
    "ttl": 300,
    "keyPattern": "users-{{.user_id}}"
  }
}

缓存配置说明:

  • enabled: 是否启用缓存
  • ttl: 缓存的生存时间(秒)
  • keyPattern: 可选的缓存键模式,支持模板语法。如果未指定,将使用请求URL和请求体的哈希作为键

重试机制

对于不稳定的API,RenderAPI提供了内置的重试机制:

{
  "request": {
    "method": "POST",
    "path": "/process"
  },
  "retry": {
    "enabled": true,
    "maxAttempts": 3,
    "initialDelay": 1000,
    "backoffFactor": 2
  }
}

重试配置说明:

  • enabled: 是否启用重试
  • maxAttempts: 最大尝试次数
  • initialDelay: 首次重试前的延迟(毫秒)
  • backoffFactor: 退避因子,用于计算后续重试的延迟时间

项目结构

RenderAPI/
├── cmd/                # 命令行工具
│   └── httpclient/     # HTTP客户端命令行工具
├── pkg/                # 核心包
│   ├── client/         # HTTP客户端实现
│   ├── template/       # 模板引擎
│   ├── hooks/          # 请求/响应钩子
│   │   ├── hooks.go         # 钩子接口和通用功能
│   │   ├── custom_hook.go   # 自定义钩子实现
│   │   ├── js_hook.go       # JavaScript钩子实现
│   │   └── cmd_hook.go      # 命令行钩子实现
│   └── config/         # 配置管理
├── examples/           # 使用示例
│   ├── basic/          # 基本使用示例
│   ├── advanced/       # 高级功能示例
│   └── template_file/  # 模板文件示例
├── testdata/           # 测试数据
└── internal/           # 内部工具和辅助函数
    └── utils/          # 工具函数

测试

RenderAPI 包含详尽的单元测试和集成测试。运行以下命令来执行测试:

# 运行所有测试
make test

# 生成测试覆盖率报告
make test-coverage

# 运行基准测试
make bench

使用场景

RenderAPI 特别适用于以下场景:

  1. API 客户端开发:轻松构建与复杂 API 交互的客户端
  2. 自动化测试:通过模板定义请求,简化 API 测试
  3. API 代理:动态转换和处理 API 请求
  4. 数据采集:从多个 API 源收集和处理数据

贡献

欢迎提交Issues和Pull Requests!

许可证

本项目基于 MIT 许可证发布 - 详情请查看 LICENSE 文件。

About

RenderAPI 是一个强大的 Go 语言 HTTP 客户端库,专为模板驱动的 API 请求设计。它允许用户通过 JSON 模板定义 HTTP 请求,支持动态数据插入和请求转换。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published