V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  ryalu  ›  全部回复第 1 页 / 共 21 页
回复总数  401
1  2  3  4  5  6  7  8  9  10 ... 21  
23 小时 36 分钟前
回复了 Geeksun2021 创建的主题 摄影 今天晚上竟然用无人机拍到了闪电!
好看👍
27 天前
回复了 CareFreeSc 创建的主题 问与答 思想碰撞,唠唠
碎碎念 + 1

====
昨晚刷到汤姆哈迪主演的《黑帮领地》,还不错,熬到两点刷到了最新集,还有两集第一季就完结了,盼更中.....
====
所有权属于公司吧,之前有个类似的讨论 https://www.v2ex.com/t/676616
@Pipecraft #7 挺喜欢大佬之前 v2hot 站的,能看 3/7/30 天最热等等,虽然不维护但还是感谢大佬
53 天前
回复了 Frankcox 创建的主题 生活 父亲走了
节哀
83 天前
回复了 zy0829 创建的主题 职场话题 作为普通的程序员怎么“舔”领导
《自洽的程序员》- https://www.v2ex.com/t/1104211
84 天前
回复了 ljzxloaf 创建的主题 程序员 protobuf 不支持泛型?
@wunonglin #16 这是 json 的,如果请求协议里是 application/proto 序列化编码方式就不行了。
em....公司**需求要支持 json 和 proto 两种序列化方式(屎山),分享下实现(性能不算好,能用就行),在 encode 的地方动态构造 proto 对象,这样就不用所有 response 包一层了。

```
package http

import (
"bytes"
"fmt"
"io"
"log/slog"
stdhttp "net/http"
"reflect"
"strconv"
"strings"
"sync"
"time"

"github.com/go-kratos/kratos/v2/encoding"
ejson "github.com/go-kratos/kratos/v2/encoding/json"
eproto "github.com/go-kratos/kratos/v2/encoding/proto"
"github.com/go-kratos/kratos/v2/errors"
"github.com/go-kratos/kratos/v2/transport/http"
pproto "github.com/golang/protobuf/proto"
"github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/desc/builder"
"github.com/jhump/protoreflect/dynamic"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"

".../api/_gen/go/ecode"
)

var (
messagePool = &sync.Map{}
defaultErrorMessageDescriptor *desc.MessageDescriptor

// MarshalOptions is a configurable JSON format marshaller.
jsonMarshalOptions = protojson.MarshalOptions{
EmitUnpopulated: true,
}
// UnmarshalOptions is a configurable JSON format parser.
jsonUnmarshalOptions = protojson.UnmarshalOptions{
DiscardUnknown: true,
}

jsonCodecHeaders = []string{"application/json", "text/json"}
protoCodecHeaders = []string{"application/x-protobuf", "application/proto", "application/octet-stream"}
jsonCodec = encoding.GetCodec(ejson.Name)
protoCodec = encoding.GetCodec(eproto.Name)
registeredCodecs = make(map[string]encoding.Codec)
)

func init() {
for _, contentType := range jsonCodecHeaders {
registeredCodecs[contentType] = jsonCodec
}
for _, contentType := range protoCodecHeaders {
registeredCodecs[contentType] = protoCodec
}

defaultErrorMessageDescriptor, _ = builder.NewMessage("Response").
AddField(builder.NewField("code", builder.FieldTypeInt32()).SetNumber(1)).
AddField(builder.NewField("message", builder.FieldTypeString()).SetNumber(2)).
AddField(builder.NewField("ts", builder.FieldTypeInt64()).SetNumber(3)).Build()
}

func requestDecoder(r *http.Request, v any) error {
codec, _, ok := codecForRequest(r, "Content-Type")
if !ok {
return errors.BadRequest("CODEC", fmt.Sprintf("unregister Content-Type: %s", r.Header.Get("Content-Type")))
}

data, err := io.ReadAll(r.Body)
if err != nil {
return errors.BadRequest("CODEC", err.Error())
}

if len(data) == 0 {
return nil
}

if err = codec.Unmarshal(data, v); err != nil {
return errors.BadRequest("CODEC", fmt.Sprintf("body unmarshal err: %s, body: %s", err.Error(), string(data)))
}

r.Body = io.NopCloser(bytes.NewBuffer(data))
return nil
}

func ErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {
er := errors.FromError(err)

// 获取业务错误码
code, ok := ecode.ServiceErrorReason_value[er.Reason]
if !ok || code == 0 { // 异常情况直接使用 errors.code
code = er.Code
}

codec, contentType, ok := codecForRequest(r, "Accept")
if !ok {
codec, contentType, _ = codecForRequest(r, "Content-Type")
}

switch codec.Name() {
case ejson.Name:
bt, err := encodeJSONResponse(code, er.Message, []byte("{}"))
if err != nil {
slog.Error("fail to encode json response: %v", err)
w.WriteHeader(stdhttp.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", contentType)
w.WriteHeader(stdhttp.StatusOK)
_, _ = w.Write(bt)
return

case eproto.Name:
bt, err := encodeProtoResponse(code, er.Message, nil)
if err != nil {
slog.Error("fail to encode json response: %v", err)
w.WriteHeader(stdhttp.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", contentType)
w.WriteHeader(stdhttp.StatusOK)
_, _ = w.Write(bt)
return

}

return
}

func responseEncoder(w http.ResponseWriter, r *http.Request, i any) error {
codec, contentType, ok := codecForRequest(r, "Accept")
if !ok {
codec, contentType, _ = codecForRequest(r, "Content-Type")
}

m, ok := i.(proto.Message)
if !ok {
return errors.BadRequest("CODEC", fmt.Sprintf("response is not proto.Message: %s", reflect.TypeOf(i)))
}

switch codec.Name() {
case ejson.Name:
data, err := jsonMarshalOptions.Marshal(m)
if err != nil {
return err
}

bt, err := encodeJSONResponse(200, "success", data)
if err != nil {
return err
}

w.Header().Set("Content-Type", contentType)
w.WriteHeader(stdhttp.StatusOK)
_, _ = w.Write(bt)
return nil

case eproto.Name:
bt, err := encodeProtoResponse(200, "success", m)
if err != nil {
return err
}

w.Header().Set("Content-Type", contentType)
w.WriteHeader(stdhttp.StatusOK)
_, _ = w.Write(bt)
return nil

}
return nil
}

// get codec for request
func codecForRequest(r *http.Request, name string) (encoding.Codec, string, bool) {
contentType := r.Header.Get(name)
right := strings.Index(contentType, ";")
if right == -1 {
right = len(contentType)
}

c := contentType[:right]
codec := registeredCodecs[c]
if codec != nil {
return codec, c, true
}
return jsonCodec, "application/json", false
}

func encodeJSONResponse(code int32, message string, data []byte) ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteString("{\"code\":")
buf.WriteString(strconv.FormatInt(int64(code), 10))
buf.WriteString(",\"message\":\"")
buf.WriteString(message)
buf.WriteString("\",\"ts\":" + strconv.FormatInt(time.Now().Unix(), 10) + ",")
buf.WriteString("\"data\":")
buf.Write(data)
buf.WriteString("}")
return buf.Bytes(), nil
}

func encodeProtoResponse(code int32, message string, data proto.Message) ([]byte, error) {
build, err := getProtoBuilder(data)
if err != nil {
return nil, err
}

response := dynamic.NewMessage(build)
response.SetFieldByNumber(1, code)
response.SetFieldByNumber(2, message)
response.SetFieldByNumber(3, int32(time.Now().Unix()))
if data != nil {
_ = response.TrySetFieldByNumber(4, data)
}
return response.Marshal()
}

func getProtoBuilder(message proto.Message) (*desc.MessageDescriptor, error) {
if message == nil {
return defaultErrorMessageDescriptor, nil
}

key := message.ProtoReflect().Type().Descriptor().Name()
v, ok := messagePool.Load(key)
if !ok || v == nil {
anyDesc, err := desc.LoadMessageDescriptorForMessage(pproto.MessageV1(message))
if err != nil {
return nil, fmt.Errorf("loadMessageDescriptorForMessage err: %w", err)
}
build, err := builder.NewMessage("Response").
AddField(builder.NewField("code", builder.FieldTypeInt32()).SetNumber(1)).
AddField(builder.NewField("message", builder.FieldTypeString()).SetNumber(2)).
AddField(builder.NewField("ts", builder.FieldTypeInt64()).SetNumber(3)).
AddField(builder.NewField("data", builder.FieldTypeImportedMessage(anyDesc)).SetNumber(4)).Build()
if err != nil {
return nil, fmt.Errorf("build new message err: %w", err)
}

messagePool.Store(key, build)
return build, nil
}
return v.(*desc.MessageDescriptor), nil
}

```
建议直接一次两个阻生都拔了,省得折腾两次。拔这种牙尽量去正规医院挂个主任医生的号,之前一次拔过三个,两个阻生和上面一个出来的,费用靠近 2000 左右
87 天前
回复了 cxhello 创建的主题 Go 编程语言 Go 框架使用调研
@sky3hao9 #44 如果能把框架内集成的 log 那一套直接替换成 slog 就更爽了
感谢分享,希望能有后续的更新~例如招聘、面试过程等等
91 天前
回复了 CKAJ555 创建的主题 生活 兄弟们 大家平时都用类似大宝的润肤霜吗
标婷维生素 E 乳、京润珍珠保湿霜
来自果子的人文关怀

https://i.imgur.com/mjQXjT2.png
108 天前
回复了 Flowing 创建的主题 生活 我想问问 有多少人是和爱情结婚的
相信,但不觉得会发生在自己身上
113 天前
回复了 notapple 创建的主题 macOS 求推荐 macOS 必装软件
推荐两个小而美的 App
PrettyClean: 轻量小巧的清理、卸载工具。
pap.er:壁纸应用
既然你是程序员而且能上 v 站,就说明你的认知能力已经超过大部分人了。
先基于自己财产状况想好自己能承受的出资额度呗,然后再和父母好好沟通下,交个底自己能出多少,看看其他人能帮出多少借多少还差多少,帮规划下这么多钱能建到什么程度(不一定要一次投入几十万把房子建好,我们那边去年邻居光水泥现浇的一层大平层架子也就在十七八万左右吧),网上搜集下优化成本的技巧.....

emm...看情况父母拉扯大三个确实也挺不容易的,毕竟不是每个父母都能成为百万富翁,可能父母觉得孩子都大了自己也老了,趁现在自己还稍微能动,最后帮他们把个房子弄起来,不然以后要是带人回来,房间都没得一个.....都是至亲,不要太看重钱,毕竟当你真正有难的时候,他们才是真心且帮的上你的一群人。

以上仅是我看帖子所想到的片面之词,仅供参考。轻喷(手动狗头.jpg )
1  2  3  4  5  6  7  8  9  10 ... 21  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3503 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 52ms · UTC 00:45 · PVG 08:45 · LAX 17:45 · JFK 20:45
Developed with CodeLauncher
♥ Do have faith in what you're doing.