1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-elton

Клонировать/Скачать
custom_body_parser.md 8.5 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 04.06.2025 23:59 7f2703a
description
Пользовательский Body Parser

elton-body-parser предоставляет обработку только для application/json и application/x-www-form-urlencoded, преобразуя их в json-байты. В реальных сценариях существуют и другие случаи, например, xml или пользовательские структуры данных.

В реальных проектах статистические данные обычно записываются в influxdb. Для улучшения производительности данные отправляются пакетами (например, каждые 1000 точек статистики). При отправке данных часто встречаются повторяющиеся символы, что приводит к увеличению использования пропускной способности. Для уменьшения этого эффекта данные сжимаются. Учитывая производительность, используется сжатие snappy. Ниже приведен пример кода:

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/golang/snappy"
	"github.com/vicanso/elton"
)

// Просто пример, для ошибок используется panic
func post() {
	// weather,location=us-midwest temperature=82 1465839830100400200
	max := 1000
	arr := make([]string, max)
	for i := 0; i < max; i++ {
		arr[i] = "weather,location=us-midwest temperature=82 " + strconv.FormatInt(time.Now().UnixNano(), 10)
	}
	var dst []byte
	data := snappy.Encode(dst, []byte(strings.Join(arr, "\n")))

	req, err := http.NewRequest("POST", "http://127.0.0.1:3000/influx", bytes.NewReader(data))
	req.Header.Set(elton.HeaderContentType, ContentTypeIfx)
	if err != nil {
		panic(err)
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	result, _ := io.ReadAll(resp.Body)
	fmt.Println(string(result))
}

const (
	// ContentTypeIfx тип данных influx
	ContentTypeIfx = "application/ifx"
)
``````go
// NewInfluxParser создает influx parser
func NewInfluxParser() elton.Handler {
	return func(c *elton.Context) (err error) {
		// Для не-POST запросов или несоответствующих типов данных пропускаем
		if c.Request.Method != http.MethodPost ||
			c.GetRequestHeader(elton.HeaderContentType) != ContentTypeIfx {
			return c.Next()
		}
		body, err := io.ReadAll(c.Request.Body)
		// Если произошла ошибка при чтении данных, возвращаемся
		if err != nil {
			return
		}
		var dst []byte
		data, err := snappy.Decode(dst, body)
		// Если произошла ошибка при распаковке, возвращаемся (можно также определить пользовательский тип ошибки для упрощения отладки)
		if err != nil {
			return
		}
		// Теперь данные распакованы
		c.RequestBody = data
		return c.Next()
	}
}

func main() {
	e := elton.New()
	go func() {
		// Ожидание одной секунды для запуска elton (только для удобства тестирования, одинаковый код для клиента и сервера)
		time.Sleep(time.Second)
		post()
	}()
}
``````go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/golang/snappy"
	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

// Просто пример, для ошибок используется panic
func post() {
	// weather,location=us-mидвэст temperature=82 1465839830100400200
	max := 1000
	arr := make([]string, max)
	for i := 0; i < max; i++ {
		arr[i] = "weather,location=us-midwest temperature=82 " + strconv.FormatInt(time.Now().UnixNano(), 10)
	}
	var dst []byte
	data := snappy.Encode(dst, []byte(strings.Join(arr, "\n")))

	req, err := http.NewRequest("POST", "http://127.0.0.1:3000/influx", bytes.NewReader(data))
	req.Header.Set(elton.HeaderContentType, ContentTypeIfx)
	if err != nil {
		panic(err)
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	result, _ := io.ReadAll(resp.Body)
	fmt.Println(string(result))
}

const (
	// ContentTypeIfx тип данных influx
	ContentTypeIfx = "application/ifx"
)

func main() {
	e := elton.New()
	e.Use(NewInfluxParser())

	e.POST("/influx", func(c *elton.Context) (err error) {
		points := strings.SplitN(string(c.RequestBody), "\n", -1)
		c.BodyBuffer = bytes.NewBufferString("добавлено " + strconv.Itoa(len(points)) + " точек данных в influxdb")
		return
	})

	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

С помощью различных пользовательских middleware можно реализовать разные способы парсинга данных. Достаточно сохранить результат парсинга в Context.RequestBody, а затем последующие функции преобразуют байты в соответствующие структуры данных, что делает процесс простым и удобным.

elton-body-parser предоставляет возможность создания пользовательских декодеров, которые можно добавлять в зависимости от потребностей. Реализация выше может быть упрощена следующим образом:

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/golang/snappy"
	"github.com/vicanso/elton"
	"github.com/vicanso/elton/middleware"
)

// Просто пример, для ошибок используется panic
func post() {
	// weather,location=us-midwest temperature=82 1465839830100400200
	max := 1000
	arr := make([]string, max)
	for i := 0; i < max; i++ {
		arr[i] = "weather,location=us-midwest temperature=82 " + strconv.FormatInt(time.Now().UnixNano(), 10)
	}
	var dst []byte
	data := snappy.Encode(dst, []byte(strings.Join(arr, "\n")))

	req, err := http.NewRequest("POST", "http://127.0.0.1:3000/influx", bytes.NewReader(data))
	req.Header.Set(elton.HeaderContentType, ContentTypeIfx)
	if err != nil {
		panic(err)
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	result, _ := io.ReadAll(resp.Body)
	fmt.Println(string(result))
}

const (
	// ContentTypeIfx тип данных influx
	ContentTypeIfx = "application/ifx"
)
``````go
func main() {
	e := elton.New()
	go func() {
		// Ожидание одной секунды для запуска elton (только для удобства тестирования, одинаковый код для клиента и сервера)
		time.Sleep(time.Second)
		post()
	}()
}
``````markdown
conf := middleware.BodyParserConfig{
    // Установка типов контента, которые будут обрабатываться, по умолчанию обрабатывается только application/json
    ContentTypeValidate: func(c *elton.Context) bool {
        ct := c.GetRequestHeader(elton.HeaderContentType)
        return regexp.MustCompile("application/json|" + ContentTypeIfx).MatchString(ct)
    },
}
// Распаковка gzip
conf.AddDecoder(middleware.NewGzipDecoder())
// Распаковка json
conf.AddDecoder(middleware.NewJSONDecoder())
// Добавление пользовательского декодера для influx
conf.AddDecoder(&middleware.BodyDecoder{
    // Проверка соответствия декодеру
    Validate: func(c *elton.Context) bool {
        return c.GetRequestHeader(elton.HeaderContentType) == ContentTypeIfx
    },
    // Распаковка snappy
    Decode: func(c *elton.Context, originalData []byte) (data []byte, err error) {
        var dst []byte
        data, err = snappy.Decode(dst, originalData)
        return
    },
})
``````md
	e.Use(middleware.NewBodyParser(conf))
``````go
	e.POST("/influx", func(c *elton.Context) (err error) {
		points := strings.SplitN(string(c.RequestBody), "\n", -1)
		c.BodyBuffer = bytes.NewBufferString("добавлено " + strconv.Itoa(len(points)) + " точек в influxdb завершено")
		return
	})

	err := e.ListenAndServe(":3000")
	if err != nil {
		panic(err)
	}
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-elton.git
git@api.gitlife.ru:oschina-mirror/mirrors-elton.git
oschina-mirror
mirrors-elton
mirrors-elton
master