如何更好地利用encoding/json的UnmarshalTypeError中的偏移值来进行错误处理?

huangapple go评论73阅读模式
英文:

How can I make use of offset value in enconding/json's UnmarshalTypeError for better error handling?

问题

一年多前,Go语言在json.UnmarshalTypeError类型中添加了一个Offset值(有关上下文,请参见已关闭的问题此处)。Offset值的目的是合理的,但我不确定在读取Go语言的HTTP响应体(类型为io.ReadCloser)时如何使用它。

// UnmarshalTypeError描述了一个不适合特定Go类型的JSON值。
type UnmarshalTypeError struct {
    Value  string       // JSON值的描述 - "bool", "array", "number -5"
    Type   reflect.Type // 无法分配给它的Go值的类型
    Offset int64        // 在读取Offset字节后发生错误
}

例如:

var body CustomType
decoderErr := json.NewDecoder(response.Body).Decode(&body)

if decoderErr != nil {
    if typeError, ok := decoderErr.(*json.UnmarshalTypeError); ok {
        // 在这里使用typeError.Offset做一些操作
    }
}

在捕获到错误的地方,我已经通过json.NewDecoder...response.Body中读取了数据。我正在寻找一种方法,通过使用typeError中的Offset值,再次读取response.Body,但只读取到错误发生的位置。

英文:

A little over a year ago, Go added an Offset value to the json.UnmarshalTypeError type (see closed issue here for context). The purpose behind the offset value makes sense, but I'm not sure how it can be used when reading a go http response body, which is of type io.ReadCloser.

// An UnmarshalTypeError describes a JSON value that was
// not appropriate for a value of a specific Go type.
type UnmarshalTypeError struct {
    Value  string       // description of JSON value - "bool", "array",   "number -5"
    Type   reflect.Type // type of Go value it could not be assigned to
    Offset int64        // error occurred after reading Offset bytes
}

For example:

var body CustomType
decoderErr := json.NewDecoder(response.Body).Decode(&body)

if decoderErr != nil {

	if typeError, ok := decoderErr.(*json.UnmarshalTypeError); ok {
		// Do something with typeError.Offset here
	}


}

At the point of the error getting caught, I've already read from response.Body via json.NewDecoder.... I'm looking for a way to read response.Body again, but only up to the point of the error by using the Offset value in typeError.

答案1

得分: 5

由于您想要重用请求体,您应该在解析请求体之前读取并存储请求体,这样如果存在JSON语法或类型错误,您可以使用之前存储的请求体返回更有用的错误信息。

概念验证:

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

type Hello struct {
	Name    string `json:"name"`
	Message string `json:"message"`
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		b, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "读取请求体错误", 400)
			return
		}

		// 存储请求体
		storedBody := make([]byte, len(b))
		copy(storedBody, b)

		h := &Hello{}
		if err := json.Unmarshal(b, &h); err != nil {
			var msg string
			switch t := err.(type) {
			case *json.SyntaxError:
				jsn := string(storedBody[0:t.Offset])
				jsn += "<--(无效字符)"
				msg = fmt.Sprintf("偏移量 %v 处有无效字符\n %s", t.Offset, jsn)
			case *json.UnmarshalTypeError:
				jsn := string(storedBody[0:t.Offset])
				jsn += "<--(无效类型)"
				msg = fmt.Sprintf("偏移量 %v 处的值无效\n %s", t.Offset, jsn)
			default:
				msg = err.Error()
			}
			http.Error(w, msg, 400)
			return
		}

		w.Write([]byte(`准备就绪!`))
	})

	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Fatal(err)
	}
}

希望对您有所帮助!

英文:

Since you want to reuse the request body you should read and store the body before you Unmarshal the body, then if there is a JSON syntax or type error you can return a more useful error using the body you previously stored.

Proof of concept:

package main
import (
&quot;encoding/json&quot;
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;log&quot;
&quot;net/http&quot;
)
type Hello struct {
Name    string `json:&quot;name&quot;`
Message string `json:&quot;message&quot;`
}
func main() {
http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, &quot;Error reading body&quot;, 400)
return
}
h := &amp;Hello{}
if err := json.Unmarshal(b, &amp;h); err != nil {
var msg string
switch t := err.(type) {
case *json.SyntaxError:
jsn := string(b[0:t.Offset])
jsn += &quot;&lt;--(Invalid Character)&quot;
msg = fmt.Sprintf(&quot;Invalid character at offset %v\n %s&quot;, t.Offset, jsn)
case *json.UnmarshalTypeError:
jsn := string(b[0:t.Offset])
jsn += &quot;&lt;--(Invalid Type)&quot;
msg = fmt.Sprintf(&quot;Invalid value at offset %v\n %s&quot;, t.Offset, jsn)
default:
msg = err.Error()
}
http.Error(w, msg, 400)
return
}
w.Write([]byte(`Good to go!`))
})
if err := http.ListenAndServe(&quot;:8000&quot;, nil); err != nil {
log.Fatal(err)
}
}

huangapple
  • 本文由 发表于 2016年11月23日 01:41:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/40748301.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定