分类 编程语言 中的文章

如何优雅地重复读取HTTP响应体

背景介绍 在Go语言中处理HTTP请求时,我们经常需要多次读取响应体(Response Body)的内容。然而,默认情况下Response Body只能被读取一次,这是因为它实现了io.ReadCloser接口,读取完毕后数据就会被消费掉。本文将介绍一个优雅的解决方案。 问题描述 假设我们有以下场景: 需要在日志中记录API的响应内容 需要对响应内容进行多次处理 需要在中间件中查看响应内容,同时不影响后续处理 解决方案 我们可以使用io.TeeReader来实现响应体的复制,这样就能多次读取相同的内容。以下是具体实现: package main import ( "bytes" "fmt" "io" "net/http" ) func DupReadCloser(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser) { var buf bytes.Buffer tee := io.TeeReader(reader, &buf) return io.NopCloser(tee), io.NopCloser(&buf) } func DupResponseBody(resp *http.Response) ([]byte, error) { var buf io.ReadCloser resp.Body, buf = DupReadCloser(resp.Body) data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } resp.Body = buf return data, nil } 代码解析 DupReadCloser函数:接收一个io.……

阅读全文

golang json.Marshal(error) 返回 `{}`, 问题分析与解决方案

序列化 Go 语言中的 error 接口问题:问题发现、原因与解决方案 在开发 Go 语言项目时,我们常常需要将结构体序列化为 JSON 格式。然而,当结构体中包含 error 接口时,序列化结果往往会不如预期。在这篇博客中,我们将讨论这个问题的原因,并提供两种解决方案。 问题描述 我们有一个结构体 CustomError,其中包含一个 error 类型的字段。如下所示: package main import ( "encoding/json" "fmt" ) type CustomError struct { UserID int64 Err error } func (e *CustomError) Error() string { return fmt.Sprintf("user %d: %s", e.UserID, e.Err.Error()) } func main() { var errs []CustomError errs = append(errs, CustomError{UserID: 1, Err: fmt.Errorf("error 1")}) errs = append(errs, CustomError{UserID: 2, Err: fmt.Errorf("error 2")}) errs = append(errs, CustomError{UserID: 3, Err: fmt.……

阅读全文

golang 使用反射处理 string, 用于 request body 中的 string 字段进行 trim

将 struct 中的string 字段进行 trim func trimSpace(any interface{}) { ov := reflect.ValueOf(any) if ov.Kind() == reflect.Ptr && !ov.IsNil() { ov = ov.Elem() } ot := reflect.TypeOf(any) if ot.Kind() == reflect.Ptr { ot = ot.Elem() } for i := 0; i < ot.NumField(); i++ { field := ov.Field(i) if field.Kind() == reflect.Ptr { if field.Elem().Kind() == reflect.Struct { trimSpace(field.Interface()) continue } field = field.Elem() } if field.CanInterface() && field.IsValid() { if field.……

阅读全文

golang 处理图片

支持不同格式之间的转换 支持图片像素的压缩以及拉长 package main import ( "bytes" "errors" "fmt" "image" "image/jpeg" "image/png" "io/ioutil" "os" "golang.org/x/image/draw" _ "golang.org/x/image/webp" // for RegisterFormat ) type imageHelper struct { img image.Image fileName string format string scale draw.Scaler } func NewImageHelper(fileName string, scale ...draw.Scaler) *imageHelper { var s draw.Scaler if scale == nil { s = draw.ApproxBiLinear } else { s = scale[0] } return &imageHelper{ fileName: fileName, scale: s, } } func (r *imageHelper) GuessFileFormat() (string, error) { if r.……

阅读全文

golang 合并多个文件

package main import ( "fmt" "github.com/coreos/pkg/progressutil" "io" "os" "path/filepath" "time" ) func combineMultipleFiles(outPutFile string, files ...string) { abs, err := filepath.Abs(outPutFile) if err != nil { panic("can not get outPutFile absolute path") } dir := filepath.Dir(abs) if _, err := os.Stat(dir); err != nil { if os.IsNotExist(err) { err := os.MkdirAll(dir, os.ModePerm) if err != nil { panic(fmt.Sprintf("can not mkdirs, path: %s, err: %s", dir, err)) } } } file, err := os.……

阅读全文

Python 枚举使用

from enum import Enum class Gender(Enum): male = 0 female = 1 print(Gender(0)) # Gender.male print(Gender(0).name) # female print(Gender(0).value) # 0 class EnumWithLabel(Enum): """ 允许带 label 的枚举类型, 参考: https://docs.python.org/zh-cn/3/library/enum.html#when-to-use-new-vs-init """ def __new__(cls, value, label): obj = object.__new__(cls) obj._value_ = value obj.label = label return obj class GenderWithLabel(EnumWithLabel): male = (0, "MAN") female = (1, "WOMAN") print(GenderWithLabel(0)) # GenderWithLabel.male print(GenderWithLabel(0).value) # 0 print(GenderWithLabel(0).label) # MAN class EnumWithDefault(Enum): """ 允许返回默认值的枚举类型, 不存在枚举值时不会报错 如果需要修改默认值则需要覆盖 new_default_obj 函数 """ @classmethod def _missing_(cls, value): new_member = cls.……

阅读全文

golang validator 支持翻译以及使用 json tag

package main import ( "encoding/json" "fmt" "github.com/go-playground/locales/en" "github.com/go-playground/locales/zh" "github.com/go-playground/locales/zh_Hant_TW" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" entranslations "github.com/go-playground/validator/v10/translations/en" zhtranslations "github.com/go-playground/validator/v10/translations/zh" zhtwtranslations "github.com/go-playground/validator/v10/translations/zh_tw" "reflect" "strings" ) type InternalAccountTransferRequest struct { Name string `json:"name" binding:"required"` Age string `json:"age_test" binding:"required"` Class string `binding:"required"` } func main() { s := `{"name": "123"}` var requset InternalAccountTransferRequest json.Unmarshal([]byte(s), &requset) //requset.Class = "123" v := validator.New() var err error uni := ut.New(en.New(), en.New(), zh_Hant_TW.New(), zh.New()) zh_, _ := uni.GetTranslator("zh") zhtranslations.RegisterDefaultTranslations(v, zh_) tw_, _ := uni.……

阅读全文

Mac 安装 mysqlclient

Python 版本 (venv) ➜ python --version Python 3.8.2 安装 brew install mysql export LDFLAGS="-L/usr/local/opt/openssl/lib" export CPPFLAGS="-I/usr/local/opt/openssl/include" pip install mysqlclient brew uninstall mysql ……

阅读全文

Python 中的else

if…else 最常见的 else if 1 > 0: pass else: pass for else for 循环中 只有 for 循环结束了才执行, 注意空循环也会执行 for i in range(3): print(i) else: print("end") # 0 # 1 # 2 # end for i in range(3): print(i) if i == 1: break else: print("end") # 0 # 1 try… else 这个就很好理解了, else 只会在 try 未发生任何异常的时候执行 finally 在所有状态下都会执行 try: 1 except Exception as e: print(e) else: print("else") finally: print("finally") # else # finally try: 1/0 except Exception as e: print(e) else: print("else") finally: print("finally") # division by zero # finally ……

阅读全文

python 异常处理

异常处理 def demo(): try: 1 / 0 except Exception as e: print(type(e)) #<class 'ZeroDivisionError'> 自定义异常 class CustomError(Exception): pass 异常链 class CustomError(Exception): pass def demo(): try: 1 / 0 except Exception as e: print(type(e)) #<class 'ZeroDivisionError'> raise CustomError() from e <class 'ZeroDivisionError'> Traceback (most recent call last): File "/Users/tiger/work/customer/dd.py", line 23, in demo 1 / 0 ZeroDivisionError: division by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/Users/tiger/work/customer/dd.……

阅读全文