metadata

package
v0.19.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

* @Author: kamalyes 501893067@qq.com * @Date: 2025-12-20 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2025-12-20 15:55:00 * @FilePath: \go-toolbox\pkg\metadata\accessor.go * @Description: 元数据访问器 * * Copyright (c) 2025 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2025-12-20 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2025-12-20 15:55:00 * @FilePath: \go-toolbox\pkg\metadata\converter.go * @Description: 元数据转换工具 * * Copyright (c) 2025 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2025-12-20 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2025-12-20 16:50:00 * @FilePath: \go-toolbox\pkg\metadata\extractor.go * @Description: HTTP 请求元数据提取器 * * Copyright (c) 2025 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2026-01-09 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2026-01-16 12:01:15 * @FilePath: \go-toolbox\pkg\metadata\language.go * @Description: 语言信息提取和处理 * * Copyright (c) 2026 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2025-12-20 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2025-12-20 15:55:00 * @FilePath: \go-toolbox\pkg\metadata\types.go * @Description: HTTP 请求元数据结构定义 * * Copyright (c) 2025 by kamalyes, All Rights Reserved.

* @Author: kamalyes 501893067@qq.com * @Date: 2025-12-20 00:00:00 * @LastEditors: kamalyes 501893067@qq.com * @LastEditTime: 2025-12-20 16:05:00 * @FilePath: \go-toolbox\pkg\metadata\utils.go * @Description: 元数据工具函数 * * Copyright (c) 2025 by kamalyes, All Rights Reserved.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExtractLanguage

func ExtractLanguage(r *http.Request) string

ExtractLanguage 快捷函数:使用默认配置提取语言 优先级:Query(lang/language) → Header(X-Language) → Cookie(language) → Accept-Language → "en"

func ExtractLanguageWithDefault

func ExtractLanguageWithDefault(r *http.Request, defaultLang string) string

ExtractLanguageWithDefault 快捷函数:使用指定默认语言提取

func FromAcceptLanguageSource

func FromAcceptLanguageSource(ctx context.Context, r *http.Request, key string) string

FromAcceptLanguageSource 从 Accept-Language header 中提取语言 例如: "zh-CN,zh;q=0.9,en;q=0.8" -> "zh-CN"

func FromContextSource

func FromContextSource(ctx context.Context, r *http.Request, key string) string

FromContextSource 从 context 中提取字符串值

func FromCookieSource

func FromCookieSource(ctx context.Context, r *http.Request, key string) string

FromCookieSource 从 HTTP cookie 中提取值

func FromHeaderSource

func FromHeaderSource(ctx context.Context, r *http.Request, key string) string

FromHeaderSource 从 HTTP header 中提取值

func FromQuerySource

func FromQuerySource(ctx context.Context, r *http.Request, key string) string

FromQuerySource 从 URL query 参数中提取值

直接扫原始查询串,不走 r.URL.Query()

r.URL.Query() 会把整个查询串解析成一张 url.Values(map[string][]string), 每个键一次调用就建一张 map而取语言要查 lang 与 language 两个键,于是同一个 查询串每次请求要解析两遍、建两张 map 这里按 url.ParseQuery 的规则线性扫一遍,首个匹配即返回,不分配 map

语义与 url.Values.Get 对齐:按 & 切分、按首个 = 切分、键与值都要经过 QueryUnescape、解码失败的那一对跳过(ParseQuery 会记录错误但不收录该对)、 多个同名键取第一个这几条由 TestQueryGetMatchesStdlib 差分验证

[EN] Scans the raw query string instead of going through r.URL.Query(), which parses the whole string into a url.Values map — one map per call, and looking up a language checks both lang and language, so the same query string was parsed twice per request. This walks it once by url.ParseQuery's rules and returns the first match without allocating a map. The semantics match url.Values.Get — split on &, split on the first =, unescape both key and value, skip a pair that fails to unescape as ParseQuery does, and take the first of several same-named keys — all verified differentially by TestQueryGetMatchesStdlib

func GetRemoteIP

func GetRemoteIP(remoteAddr string) string

GetRemoteIP 从 RemoteAddr 中提取 IP 地址(去除端口)

func GetRemotePort

func GetRemotePort(remoteAddr string) string

GetRemotePort 从 RemoteAddr 中提取端口号

func GetTLSVersionString

func GetTLSVersionString(version uint16) string

GetTLSVersionString 将 TLS 版本号转换为可读字符串

func NormalizeLanguage

func NormalizeLanguage(lang string) string

NormalizeLanguage 标准化语言代码 例如: "zh-cn" -> "zh-CN", "zh_CN" -> "zh-CN", "EN" -> "en", "zh-hans-cn" -> "zh-Hans-CN"

func ParseAcceptLanguage

func ParseAcceptLanguage(acceptLang string) (language, region, fullTag string)

ParseAcceptLanguage 解析 Accept-Language 头获取主要语言和地区代码 返回: 语言代码(如 "zh"), 地区代码(如 "CN"), 完整标签(如 "zh-CN")

Types

type ContextKey

type ContextKey string

func (ContextKey) String

func (ck ContextKey) String() string

type LanguageExtractor

type LanguageExtractor struct {
	DefaultLanguage string
	QueryKeys       []string // Query 参数的键名,如 ["lang", "language"]
	HeaderKeys      []string // Header 的键名,如 ["X-Language"]
	CookieKey       string   // Cookie 的键名
	UseAcceptLang   bool     // 是否使用 Accept-Language header
	Normalize       bool     // 是否标准化语言代码
}

LanguageExtractor 语言提取器配置

func NewLanguageExtractor

func NewLanguageExtractor(defaultLang string) *LanguageExtractor

NewLanguageExtractor 创建默认的语言提取器

func (*LanguageExtractor) Extract

func (le *LanguageExtractor) Extract(r *http.Request) string

Extract 从请求中提取语言信息 优先级:Query → Header → Cookie → Accept-Language → 默认值

func (*LanguageExtractor) ExtractWithContext

func (le *LanguageExtractor) ExtractWithContext(r *http.Request, contextKey ContextKey) (*http.Request, string)

ExtractWithContext 从请求中提取语言信息并存入 context

type MetadataExtractor

type MetadataExtractor struct {
	// contains filtered or unexported fields
}

func NewMetadataExtractor

func NewMetadataExtractor(ctx context.Context, r *http.Request) *MetadataExtractor

NewMetadataExtractor 创建一个元数据提取器,从 context 和 request 中提取值

func NewMetadataExtractorFromRequest

func NewMetadataExtractorFromRequest(r *http.Request) *MetadataExtractor

NewMetadataExtractorFromRequest 从 http.Request 创建元数据提取器,自动使用 request 的 context

func (*MetadataExtractor) Default

func (me *MetadataExtractor) Default(val string) *MetadataExtractor

Default 设置默认值,当所有来源均未提取到值时返回该默认值

func (*MetadataExtractor) FromContext

func (me *MetadataExtractor) FromContext(key ContextKey) *MetadataExtractor

FromContext 从 context 中提取值

func (*MetadataExtractor) FromCookie

func (me *MetadataExtractor) FromCookie(key string) *MetadataExtractor

FromCookie 从 HTTP cookie 中提取值

func (*MetadataExtractor) FromHeader

func (me *MetadataExtractor) FromHeader(key string) *MetadataExtractor

FromHeader 从 HTTP header 中提取值

func (*MetadataExtractor) FromQuery

func (me *MetadataExtractor) FromQuery(key string) *MetadataExtractor

FromQuery 从 URL query 参数中提取值

func (*MetadataExtractor) Get

func (me *MetadataExtractor) Get() string

Get 执行提取操作,按添加的来源顺序尝试提取值

func (*MetadataExtractor) WithProcess

func (me *MetadataExtractor) WithProcess(process ProcessFunc) *MetadataExtractor

WithProcess 为最后添加的来源设置处理函数

type ProcessFunc

type ProcessFunc func(val string) string

type RequestMetadata

type RequestMetadata struct {
	// 基础请求信息
	UserAgent     string `json:"user_agent"`
	ClientIP      string `json:"client_ip"`
	RemoteAddr    string `json:"remote_addr"`
	RequestURI    string `json:"request_uri"`
	QueryString   string `json:"query_string"`
	RequestMethod string `json:"request_method"`
	RequestHost   string `json:"request_host"`

	// 来源信息
	Origin  string `json:"origin"`
	Referer string `json:"referer"`

	// 代理和转发信息
	XForwardedFor   string `json:"x_forwarded_for"`
	XRealIP         string `json:"x_real_ip"`
	XForwardedProto string `json:"x_forwarded_proto"`
	XForwardedHost  string `json:"x_forwarded_host"`
	XForwardedPort  string `json:"x_forwarded_port"`

	// 客户端偏好信息
	AcceptLanguage string `json:"accept_language"`
	AcceptEncoding string `json:"accept_encoding"`
	Accept         string `json:"accept"`

	// WebSocket 协议信息
	SecWebSocketKey        string `json:"sec_websocket_key"`
	SecWebSocketVersion    string `json:"sec_websocket_version"`
	SecWebSocketProtocol   string `json:"sec_websocket_protocol"`
	SecWebSocketExtensions string `json:"sec_websocket_extensions"`

	// 连接信息
	Connection string `json:"connection"`
	Upgrade    string `json:"upgrade"`

	// CDN 和安全信息
	CFRay          string `json:"cf_ray"`
	CFConnectingIP string `json:"cf_connecting_ip"`
	CFIPCountry    string `json:"cf_ipcountry"`
	XRequestID     string `json:"x_request_id"`
	XCorrelationID string `json:"x_correlation_id"`
	XDeviceID      string `json:"x_device_id"` // 设备唯一标识

	// 缓存和条件请求
	CacheControl    string `json:"cache_control"`
	IfNoneMatch     string `json:"if_none_match"`
	IfModifiedSince string `json:"if_modified_since"`

	// 客户端提示信息(Client Hints)
	SecCHUA         string `json:"sec_ch_ua"`
	SecCHUAMobile   string `json:"sec_ch_ua_mobile"`
	SecCHUAPlatform string `json:"sec_ch_ua_platform"`
	DNT             string `json:"dnt"`

	// TLS 信息
	Protocol       string `json:"protocol"`
	TLSVersion     uint16 `json:"tls_version,omitempty"`
	TLSCipherSuite uint16 `json:"tls_cipher_suite,omitempty"`
	TLSServerName  string `json:"tls_server_name,omitempty"`

	// User-Agent 解析结果(来自 useragent.Parse)
	Browser        string `json:"browser,omitempty"`         // 浏览器名称: Chrome, Firefox, Safari
	BrowserVersion string `json:"browser_version,omitempty"` // 浏览器版本号
	OS             string `json:"os,omitempty"`              // 操作系统: Windows, macOS, Android, iOS
	OSVersion      string `json:"os_version,omitempty"`      // 操作系统版本号
	Device         string `json:"device,omitempty"`          // 设备名称
	DeviceType     string `json:"device_type,omitempty"`     // 设备类型: mobile/tablet/desktop/bot
	DeviceVendor   string `json:"device_vendor,omitempty"`   // 设备厂商: Apple, Samsung, Huawei
	IsBot          bool   `json:"is_bot"`                    // 是否为爬虫/机器人
	BotName        string `json:"bot_name,omitempty"`        // 爬虫名称
	IsMobile       bool   `json:"is_mobile"`                 // 是否为移动设备
	IsTablet       bool   `json:"is_tablet"`                 // 是否为平板设备
}

RequestMetadata HTTP 请求元数据

func ExtractRequestMetadata

func ExtractRequestMetadata(r *http.Request) *RequestMetadata

ExtractRequestMetadata 从 HTTP 请求中提取所有元数据 头部键名一律写成 Go 的规范形式

http.Header.Get 会先把键名过一遍 textproto.CanonicalMIMEHeaderKey,而该函数的 规范形式是「按连字符分段、每段首字母大写其余小写」所以 X-Request-ID 并不规范, 规范形式是 X-Request-Id;CF-Ray 要写成 Cf-Ray,Sec-WebSocket-Key 要写成 Sec-Websocket-Key非规范的键名会让 Get 每次都分配一个规范化后的字符串副本

两种写法取到的值完全相同(Get 两边都会规范化),差的只是开销实测:

Get("X-Request-ID")  45.0ns / 16B / 1 次分配
Get("X-Request-Id")  15.7ns /  0B / 0 次

本函数要读三十多个头部,原先有十五个键名不规范,于是每次请求白付十五次分配

Cf-Ipcountry 这类写法看着像拼错,其实是规范形式,不要「修正」回去

[EN] Header keys are written in Go's canonical form throughout. http.Header.Get passes the key through textproto.CanonicalMIMEHeaderKey, whose canonical form upper-cases the first letter of each dash-separated token and lower-cases the rest, so X-Request-ID is not canonical — X-Request-Id is — and CF-Ray must be written Cf-Ray. A non-canonical key makes Get allocate a canonicalised copy on every call. Both spellings read the same value since Get canonicalises either way; only the cost differs, measured at 45.0ns with one allocation against 15.7ns with none. This function reads over thirty headers and fifteen of its keys were non-canonical, so every request paid fifteen needless allocations. Spellings like Cf-Ipcountry look like typos but are the canonical form and should not be "corrected" back

func FromMap

func FromMap(data map[string]interface{}) *RequestMetadata

FromMap 从 map[string]interface{} 创建 RequestMetadata(使用反射自动填充)

func (*RequestMetadata) GetHeader

func (m *RequestMetadata) GetHeader(key string) string

GetHeader 从 RequestMetadata 中获取指定的头信息

func (*RequestMetadata) SetHeader

func (m *RequestMetadata) SetHeader(key, value string)

SetHeader 设置 RequestMetadata 中指定的头信息

func (*RequestMetadata) ToMap

func (m *RequestMetadata) ToMap() map[string]interface{}

ToMap 将 RequestMetadata 转换为 map[string]interface{}

type SourceFunc

type SourceFunc func(ctx context.Context, r *http.Request, key string) string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL