Hertz 学习笔记(10)

119 阅读4分钟

今天学一下如何渲染获取到的数据

渲染 json, html, protobuf 的示例

渲染 json 的例子

/*
 * Copyright 2022 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"context"

	"github.com/cloudwego/hertz/pkg/common/utils"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main() {
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))

	// utils.H is a shortcut for map[string]interface{}
	h.GET("/someJSON", func(ctx context.Context, c *app.RequestContext) {
		c.JSON(consts.StatusOK, utils.H{"message": "hey", "status": consts.StatusOK})
	})

	h.GET("/moreJSON", func(ctx context.Context, c *app.RequestContext) {
		// You also can use a struct
		var msg struct {
			Company  string `json:"company"`
			Location string
			Number   int
		}
		msg.Company = "company"
		msg.Location = "location"
		msg.Number = 123
		// Note that msg.Company becomes "company" in the JSON
		// Will output  :   {"company": "company", "Location": "location", "Number": 123}
		c.JSON(consts.StatusOK, msg)
	})

	h.GET("/pureJson", func(ctx context.Context, c *app.RequestContext) {
		c.PureJSON(consts.StatusOK, utils.H{
			"html": "<p> Hello World </p>",
		})
	})

	h.GET("/someData", func(ctx context.Context, c *app.RequestContext) {
		c.Data(consts.StatusOK, "text/plain; charset=utf-8", []byte("hello"))
	})

	h.Spin()
}

这里面有三个 GET 请求,最有用的是第一个,里面自定义了一个 JSON 结构,然后打印一个 JSON 对象出来,默认的字段名都是大写字母开头,如果需要小写字母开头就要像 company 这个字段一样去设置。

渲染 html 的例子

我觉得这个例子和之前一天里面的有一点重复了,当时也渲染出来一个 HTML 文件。

/*
 * Copyright 2022 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"context"
	"fmt"
	"html/template"
	"net/http"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/common/utils"
)

func formatAsDate(t time.Time) string {
	year, month, day := t.Date()
	return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}

func main() {
	h := server.Default(server.WithHostPorts(":8080"))

	h.Delims("{[{", "}]}")
	h.SetFuncMap(template.FuncMap{
		"formatAsDate": formatAsDate,
	})
	h.LoadHTMLGlob("render/html/*")

	h.GET("/index", func(c context.Context, ctx *app.RequestContext) {
		ctx.HTML(http.StatusOK, "index.tmpl", utils.H{
			"title": "Main website",
		})
	})

	h.GET("/raw", func(c context.Context, ctx *app.RequestContext) {
		ctx.HTML(http.StatusOK, "template1.html", map[string]interface{}{
			"now": time.Date(2017, 0o7, 0o1, 0, 0, 0, 0, time.UTC),
		})
	})
	h.Spin()
}

然后配套的 index.tmpl 模板如下:

<html>
<h1>
    {[{ .title }]}
</h1>
</html>

配套的 template1.html 如下:

<h1>Date: {[{.now | formatAsDate}]}</h1>

要运行的话还是:

go run render/html/main.go

然后访问 http://127.0.0.1:8080/index

渲染 protobuf 的例子

目录里面的结构如下:

$ tree .
.
├── body
│   └── body.pb.go
├── README.md
├── body.proto
└── main.go

protobuf 的格式定义在 body.proto

syntax = "proto2";
package body;
option go_package = "./body";

message BodyStruct {
   optional bytes body = 1;
}

然后虚晃,还是需要 body.pb.go 文件,这里面定义格式:

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.28.0
// 	protoc        v3.19.4
// source: body.proto

package body

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type BodyStruct struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Body []byte `protobuf:"bytes,1,opt,name=body" json:"body,omitempty"`
}

func (x *BodyStruct) Reset() {
	*x = BodyStruct{}
	if protoimpl.UnsafeEnabled {
		mi := &file_body_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *BodyStruct) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*BodyStruct) ProtoMessage() {}

func (x *BodyStruct) ProtoReflect() protoreflect.Message {
	mi := &file_body_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use BodyStruct.ProtoReflect.Descriptor instead.
func (*BodyStruct) Descriptor() ([]byte, []int) {
	return file_body_proto_rawDescGZIP(), []int{0}
}

func (x *BodyStruct) GetBody() []byte {
	if x != nil {
		return x.Body
	}
	return nil
}

var File_body_proto protoreflect.FileDescriptor

var file_body_proto_rawDesc = []byte{
	0x0a, 0x0a, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x62, 0x6f,
	0x64, 0x79, 0x22, 0x20, 0x0a, 0x0a, 0x42, 0x6f, 0x64, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
	0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
	0x62, 0x6f, 0x64, 0x79, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x62, 0x6f, 0x64, 0x79,
}

var (
	file_body_proto_rawDescOnce sync.Once
	file_body_proto_rawDescData = file_body_proto_rawDesc
)

func file_body_proto_rawDescGZIP() []byte {
	file_body_proto_rawDescOnce.Do(func() {
		file_body_proto_rawDescData = protoimpl.X.CompressGZIP(file_body_proto_rawDescData)
	})
	return file_body_proto_rawDescData
}

var file_body_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_body_proto_goTypes = []interface{}{
	(*BodyStruct)(nil), // 0: body.BodyStruct
}
var file_body_proto_depIdxs = []int32{
	0, // [0:0] is the sub-list for method output_type
	0, // [0:0] is the sub-list for method input_type
	0, // [0:0] is the sub-list for extension type_name
	0, // [0:0] is the sub-list for extension extendee
	0, // [0:0] is the sub-list for field type_name
}

func init() { file_body_proto_init() }
func file_body_proto_init() {
	if File_body_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_body_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*BodyStruct); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_body_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   1,
			NumExtensions: 0,
			NumServices:   0,
		},
		GoTypes:           file_body_proto_goTypes,
		DependencyIndexes: file_body_proto_depIdxs,
		MessageInfos:      file_body_proto_msgTypes,
	}.Build()
	File_body_proto = out.File
	file_body_proto_rawDesc = nil
	file_body_proto_goTypes = nil
	file_body_proto_depIdxs = nil
}

接下来是 main.go

/*
 * Copyright 2022 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"context"

	"github.com/cloudwego/hertz-examples/render/protobuf/body"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
)

func main() {
	h := server.Default(server.WithHostPorts(":8080"))

	h.GET("/somePb", func(ctx context.Context, c *app.RequestContext) {
		body := body.BodyStruct{
			Body: []byte("Hello World"),
		}
		c.ProtoBuf(200, &body)
	})

	h.Spin()
}

要运行,先跑主函数代码起一个渲染的服务端:

go run render/protobuf/main.go

然后访问 http://127.0.0.1:8080/somePb

目前没有研究明白,目测是把那份 raw 的数组解析成一段带什么 hello hertz protobuf 之类的对象。