英文:
Go Blockchain API: invalid character 'þ' looking for beginning of value
问题
我正在处理一个属性租赁区块链项目,使用API。当我在本地向localhost:8080/contracts发出GET请求时,我收到错误信息:"invalid character 'þ' looking for beginning of value"。我在curl和Postman中都收到了这个错误。
Main.go:
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
"github.com/HartleyIntegrity/smartley-chain/api"
"github.com/HartleyIntegrity/smartley-chain/db"
)
func main() {
// 连接到MongoDB
err := db.ConnectMongoDB()
if err != nil {
log.Fatalf("连接到MongoDB时出错:%v", err)
}
// 创建一个新的路由器
router := api.NewRouter()
// 日志中间件
router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 创建一个响应日志记录器来记录响应详情
resLogger := &responseLogger{
ResponseWriter: w,
statusCode: http.StatusOK,
}
next.ServeHTTP(resLogger, r)
// 将请求和响应详情记录到控制台和文件中
logDetails := fmt.Sprintf("%s %s %d %v %s\n", r.Method, r.URL.Path, resLogger.statusCode, time.Since(start), resLogger.body)
fmt.Print(logDetails)
log.Print(logDetails)
})
})
// 恢复中间件,用于记录panic并从中恢复
router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
errMsg := fmt.Sprintf("Panic: %v", r)
log.Println(errMsg)
http.Error(w, errMsg, http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
})
// 添加日志记录以转储请求正文,以帮助调试"invalid character 'þ' looking for beginning of value"错误
router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Printf("读取请求正文时出错:%v", err)
}
log.Printf("请求正文:%s", string(bodyBytes))
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
next.ServeHTTP(w, r)
})
})
// 启动服务器
log.Println("在端口8080上启动服务器...")
log.Fatal(http.ListenAndServe(":8080", router))
}
// 定义一个自定义的响应日志记录器,用于记录响应详情
type responseLogger struct {
http.ResponseWriter
statusCode int
body string
}
func (rl *responseLogger) WriteHeader(statusCode int) {
rl.statusCode = statusCode
rl.ResponseWriter.WriteHeader(statusCode)
}
func (rl *responseLogger) Write(body []byte) (int, error) {
rl.body = string(body)
return rl.ResponseWriter.Write(body)
}
Routes.go
package api
import (
"github.com/gorilla/mux"
)
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/contracts", createContractHandler).Methods("POST")
router.HandleFunc("/contracts/{id}", getContractHandler).Methods("GET")
router.HandleFunc("/contracts", listContractsHandler).Methods("GET")
router.HandleFunc("/contracts/{id}", updateContractHandler).Methods("PUT")
router.HandleFunc("/contracts/{id}", deleteContractHandler).Methods("DELETE")
return router
}
Handlers.go
package api
import (
"bytes"
"encoding/json"
"log"
"net/http"
"github.com/HartleyIntegrity/smartley-chain/blockchain"
"github.com/HartleyIntegrity/smartley-chain/contracts"
"github.com/gorilla/mux"
)
func respondWithError(w http.ResponseWriter, code int, message string) {
respondWithJSON(w, code, map[string]string{"error": message})
}
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, err := json.Marshal(payload)
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
func createContractHandler(w http.ResponseWriter, r *http.Request) {
// 记录请求正文以帮助识别是否有导致问题的非UTF字符
defer r.Body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
log.Printf("请求正文:%s", buf.String())
// 解析请求正文以获取合同数据
var contractData blockchain.Contract
err := json.NewDecoder(buf).Decode(&contractData)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 使用提供的数据创建新合同
newContract := contracts.CreateContract(contractData)
// 响应新创建的合同
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(newContract)
}
func getContractHandler(w http.ResponseWriter, r *http.Request) {
// 从URL中获取ID
vars := mux.Vars(r)
id := vars["id"]
// 获取指定ID的合同
contract, err := contracts.GetContract(id)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
// 响应合同
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(contract)
}
func listContractsHandler(w http.ResponseWriter, r *http.Request) {
// 记录请求正文以帮助识别是否有导致问题的非UTF字符
defer r.Body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
log.Printf("请求正文:%s", buf.String())
// 获取所有合同的列表
contracts, err := contracts.ListContracts()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 响应合同列表
w.Header().Set("Content-Type", "application/json")
buf.Reset()
err = json.NewEncoder(buf).Encode(contracts)
if err != nil {
log.Printf("编码响应正文时出错:%v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
responseBody := buf.String()
w.WriteHeader(http.StatusOK)
if _, err := w.Write(buf.Bytes()); err != nil {
log.Printf("写入响应正文时出错:%v", err)
}
log.Printf("响应正文:%s", responseBody)
}
func updateContractHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
var newContractData contracts.Contract
if err := json.NewDecoder(r.Body).Decode(&newContractData); err != nil {
respondWithError(w, http.StatusBadRequest, err.Error())
return
}
defer r.Body.Close()
contract, err := contracts.UpdateContract(id, newContractData)
if err != nil {
respondWithError(w, http.StatusNotFound, err.Error())
return
}
respondWithJSON(w, http.StatusOK, contract)
}
func deleteContractHandler(w http.ResponseWriter, r *http.Request) {
// 从URL中获取ID
vars := mux.Vars(r)
id := vars["id"]
// 删除指定ID的合同
err := contracts.DeleteContract(id)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
// 响应204 No Content状态,表示合同已成功删除
w.WriteHeader(http.StatusNoContent)
}
Blockchain.go
package blockchain
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/gob"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"time"
)
type Blockchain struct {
Blocks []*Block
}
func CreateBlock(index int, data Contract, prevHash string) *Block {
timestamp := time.Now()
dataBytes := data.ToBytes()
hash := CalculateHash(data, prevHash, timestamp)
return &Block{index, timestamp.Format(time.RFC3339), dataBytes, hash, prevHash}
}
func (bc *Blockchain) AddBlock(contract Contract, prevHash string) {
prevBlock := bc.Blocks[len(bc.Blocks)-1]
dataBytes := prevBlock.Data
var data Contract
err := json.Unmarshal(dataBytes, &data)
if err != nil {
log.Fatal(err)
}
newBlock := CreateBlock(len(bc.Blocks), data, prevBlock.Hash)
bc.Blocks = append(bc.Blocks, newBlock)
}
func CalculateHash(data Contract, prevHash string, timestamp time.Time) string {
record := fmt.Sprintf("%s%d%d%s%s%s%s%f%f%f%t%s%s",
data.ID,
data.StartTime,
data.ContractDuration,
data.EndTime,
data.CurrentStage,
data.PropertyHash,
data.PropertyID,
data.PropertyAddress,
data.RentAmount,
data.DepositAmount,
data.DepositDeductions,
data.ContractSigned,
prevHash,
timestamp.Format(time.RFC3339),
)
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}
func GenerateID() string {
uuid := make([]byte, 16)
_, err := rand.Read(uuid)
if err != nil {
log.Fatal(err)
}
return hex.EncodeToString(uuid)
}
func StageCreated() string {
return "Created"
}
func NewBlockchain() *Blockchain {
genesisBlock := CreateGenesisBlock()
return &Blockchain{Blocks: []*Block{genesisBlock}}
}
func (c *Contract) ToBytes() []byte {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(c); err != nil {
log.Fatalf("编码合同时出错:%v", err)
}
return buf.Bytes()
}
func CreateGenesisBlock() *Block {
contract := Contract{
ID: GenerateID(),
StartTime: time.Now().Unix(),
ContractDuration: 12,
EndTime: time.Now().AddDate(1, 0, 0).Unix(),
CurrentStage: StageCreated(),
PropertyHash: "",
PropertyID: "",
PropertyAddress: "",
RentAmount: 0,
DepositAmount: 0,
DepositDeductions: 0,
IssuesReported: make([]Issue, 0),
NoticesSent: make([]Notice, 0),
ContractSigned: false,
}
block := CreateBlock(0, contract, "")
return block
}
func (bc *Blockchain) ReplaceBlock(oldBlock, newBlock *Block) error {
// 验证新块是否有效
if !isValidNewBlock(newBlock, oldBlock) {
return errors.New("无效的块")
}
// 用新块替换旧块
for i, b := range bc.Blocks {
if b.Hash == oldBlock.Hash {
bc.Blocks[i] = newBlock
return nil
}
}
return errors.New("未找到块")
}
func isValidNewBlock(newBlock, previousBlock *Block) bool {
// 检查块索引是否连续
if newBlock.Index != previousBlock.Index+1 {
return false
}
// 检查前一个块的哈希是否与新块的prevHash匹配
if newBlock.PrevHash != previousBlock.Hash {
return false
}
// 检查新块的哈希是否有效
newTimestamp, err := time.Parse(time.RFC3339, newBlock.Timestamp)
if err != nil {
return false
}
// 从块中解码合同数据
var data Contract
err = json.Unmarshal(newBlock.Data, &data)
if err != nil {
return false
}
// 检查新块的哈希是否有效
if CalculateHash(data, newBlock.PrevHash, newTimestamp) != newBlock.Hash {
return false
}
return true
}
我尝试在请求中设置各种头部,如content-type application/json等,但似乎没有起作用。我已经多次询问ChatGPT如何解决此问题,它只是说在某个JSON响应中存在UTF-8问题,但我无论如何都无法解决这个问题。
谢谢。
供参考的Github仓库:
https://github.com/HartleyIntegrity/smartley-chain
英文:
I am working on a property rental blockchain, with an API and when I make GET requests locally to localhost:8080/contracts I receive the error: "invalid character 'þ' looking for beginning of value". I receive this error in curl and Postman.
Main.go:
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
"github.com/HartleyIntegrity/smartley-chain/api"
"github.com/HartleyIntegrity/smartley-chain/db"
)
func main() {
// Connect to MongoDB
err := db.ConnectMongoDB()
if err != nil {
log.Fatalf("Error connecting to MongoDB: %v", err)
}
// Create a new router
router := api.NewRouter()
// Logging middleware
router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Create a response logger to log response details
resLogger := &responseLogger{
ResponseWriter: w,
statusCode: http.StatusOK,
}
next.ServeHTTP(resLogger, r)
// Log the request and response details to console and file
logDetails := fmt.Sprintf("%s %s %d %v %s\n", r.Method, r.URL.Path, resLogger.statusCode, time.Since(start), resLogger.body)
fmt.Print(logDetails)
log.Print(logDetails)
})
})
// Recovery middleware to log panics and recover from them
router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
errMsg := fmt.Sprintf("Panic: %v", r)
log.Println(errMsg)
http.Error(w, errMsg, http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
})
// Add logging to dump the request body to help debug "invalid character 'þ' looking for beginning of value" error
router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Printf("Error reading request body: %v", err)
}
log.Printf("Request body: %s", string(bodyBytes))
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
next.ServeHTTP(w, r)
})
})
// Start the server
log.Println("Starting the server on port 8080...")
log.Fatal(http.ListenAndServe(":8080", router))
}
// Define a custom response logger that logs response details
type responseLogger struct {
http.ResponseWriter
statusCode int
body string
}
func (rl *responseLogger) WriteHeader(statusCode int) {
rl.statusCode = statusCode
rl.ResponseWriter.WriteHeader(statusCode)
}
func (rl *responseLogger) Write(body []byte) (int, error) {
rl.body = string(body)
return rl.ResponseWriter.Write(body)
}
Routes.go
package api
import (
"github.com/gorilla/mux"
)
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/contracts", createContractHandler).Methods("POST")
router.HandleFunc("/contracts/{id}", getContractHandler).Methods("GET")
router.HandleFunc("/contracts", listContractsHandler).Methods("GET")
router.HandleFunc("/contracts/{id}", updateContractHandler).Methods("PUT")
router.HandleFunc("/contracts/{id}", deleteContractHandler).Methods("DELETE")
return router
}
Handlers.go
package api
import (
"bytes"
"encoding/json"
"log"
"net/http"
"github.com/HartleyIntegrity/smartley-chain/blockchain"
"github.com/HartleyIntegrity/smartley-chain/contracts"
"github.com/gorilla/mux"
)
func respondWithError(w http.ResponseWriter, code int, message string) {
respondWithJSON(w, code, map[string]string{"error": message})
}
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
response, err := json.Marshal(payload)
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
func createContractHandler(w http.ResponseWriter, r *http.Request) {
// Log the request body to help identify any non-UTF characters causing issues
defer r.Body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
log.Printf("Request body: %s", buf.String())
// Parse the request body to get the contract data
var contractData blockchain.Contract
err := json.NewDecoder(buf).Decode(&contractData)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Create a new contract using the provided data
newContract := contracts.CreateContract(contractData)
// Respond with the newly created contract
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(newContract)
}
func getContractHandler(w http.ResponseWriter, r *http.Request) {
// Get the ID from the URL
vars := mux.Vars(r)
id := vars["id"]
// Get the contract with the provided ID
contract, err := contracts.GetContract(id)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
// Respond with the contract
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(contract)
}
func listContractsHandler(w http.ResponseWriter, r *http.Request) {
// Log the request body to help identify any non-UTF characters causing issues
defer r.Body.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
log.Printf("Request body: %s", buf.String())
// Get a list of all contracts
contracts, err := contracts.ListContracts()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Respond with the list of contracts
w.Header().Set("Content-Type", "application/json")
buf.Reset()
err = json.NewEncoder(buf).Encode(contracts)
if err != nil {
log.Printf("Error encoding response body: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
responseBody := buf.String()
w.WriteHeader(http.StatusOK)
if _, err := w.Write(buf.Bytes()); err != nil {
log.Printf("Error writing response body: %v", err)
}
log.Printf("Response body: %s", responseBody)
}
func updateContractHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
var newContractData contracts.Contract
if err := json.NewDecoder(r.Body).Decode(&newContractData); err != nil {
respondWithError(w, http.StatusBadRequest, err.Error())
return
}
defer r.Body.Close()
contract, err := contracts.UpdateContract(id, newContractData)
if err != nil {
respondWithError(w, http.StatusNotFound, err.Error())
return
}
respondWithJSON(w, http.StatusOK, contract)
}
func deleteContractHandler(w http.ResponseWriter, r *http.Request) {
// Get the ID from the URL
vars := mux.Vars(r)
id := vars["id"]
// Delete the contract with the provided ID
err := contracts.DeleteContract(id)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
// Respond with a 204 No Content status to indicate that the contract was successfully deleted
w.WriteHeader(http.StatusNoContent)
}
Blockchain.go
package blockchain
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/gob"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"time"
)
type Blockchain struct {
Blocks []*Block
}
func CreateBlock(index int, data Contract, prevHash string) *Block {
timestamp := time.Now()
dataBytes := data.ToBytes()
hash := CalculateHash(data, prevHash, timestamp)
return &Block{index, timestamp.Format(time.RFC3339), dataBytes, hash, prevHash}
}
func (bc *Blockchain) AddBlock(contract Contract, prevHash string) {
prevBlock := bc.Blocks[len(bc.Blocks)-1]
dataBytes := prevBlock.Data
var data Contract
err := json.Unmarshal(dataBytes, &data)
if err != nil {
log.Fatal(err)
}
newBlock := CreateBlock(len(bc.Blocks), data, prevBlock.Hash)
bc.Blocks = append(bc.Blocks, newBlock)
}
func CalculateHash(data Contract, prevHash string, timestamp time.Time) string {
record := fmt.Sprintf("%s%d%d%s%s%s%s%f%f%f%t%s%s",
data.ID,
data.StartTime,
data.ContractDuration,
data.EndTime,
data.CurrentStage,
data.PropertyHash,
data.PropertyID,
data.PropertyAddress,
data.RentAmount,
data.DepositAmount,
data.DepositDeductions,
data.ContractSigned,
prevHash,
timestamp.Format(time.RFC3339),
)
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}
func GenerateID() string {
uuid := make([]byte, 16)
_, err := rand.Read(uuid)
if err != nil {
log.Fatal(err)
}
return hex.EncodeToString(uuid)
}
func StageCreated() string {
return "Created"
}
func NewBlockchain() *Blockchain {
genesisBlock := CreateGenesisBlock()
return &Blockchain{Blocks: []*Block{genesisBlock}}
}
func (c *Contract) ToBytes() []byte {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(c); err != nil {
log.Fatalf("failed to encode contract: %v", err)
}
return buf.Bytes()
}
func CreateGenesisBlock() *Block {
contract := Contract{
ID: GenerateID(),
StartTime: time.Now().Unix(),
ContractDuration: 12,
EndTime: time.Now().AddDate(1, 0, 0).Unix(),
CurrentStage: StageCreated(),
PropertyHash: "",
PropertyID: "",
PropertyAddress: "",
RentAmount: 0,
DepositAmount: 0,
DepositDeductions: 0,
IssuesReported: make([]Issue, 0),
NoticesSent: make([]Notice, 0),
ContractSigned: false,
}
block := CreateBlock(0, contract, "")
return block
}
func (bc *Blockchain) ReplaceBlock(oldBlock, newBlock *Block) error {
// Verify that the new block is valid
if !isValidNewBlock(newBlock, oldBlock) {
return errors.New("invalid block")
}
// Replace the old block with the new block
for i, b := range bc.Blocks {
if b.Hash == oldBlock.Hash {
bc.Blocks[i] = newBlock
return nil
}
}
return errors.New("block not found")
}
func isValidNewBlock(newBlock, previousBlock *Block) bool {
// Check that the block indices are consecutive
if newBlock.Index != previousBlock.Index+1 {
return false
}
// Check that the previous block's hash matches the new block's prevHash
if newBlock.PrevHash != previousBlock.Hash {
return false
}
// Check that the new block's hash is valid
newTimestamp, err := time.Parse(time.RFC3339, newBlock.Timestamp)
if err != nil {
return false
}
// Decode the contract data from the block
var data Contract
err = json.Unmarshal(newBlock.Data, &data)
if err != nil {
return false
}
// Check that the new block's hash is valid
if CalculateHash(data, newBlock.PrevHash, newTimestamp) != newBlock.Hash {
return false
}
return true
}
I have tried setting various headers in my request such as content-type application/json etc. and nothing seems to work. I've asked chatgpt several times to try a solution and it just says there is an UTF-8 issue in a JSON response somewhere and I cannot for the life of me figure this out.
Thank you.
Github repo for reference:
答案1
得分: 1
错误发生的原因是在blockchain.go#line22中,你设置的是结构体的字节,而不是 JSON 文本。
func CreateBlock(index int, data Contract, prevHash string) *Block {
timestamp := time.Now()
// 这里是结构体的字节,应该改为 'json.Marshal(data)'
dataBytes := data.ToBytes()
hash := CalculateHash(data, prevHash, timestamp)
return &Block{index, timestamp.Format(time.RFC3339), dataBytes, hash, prevHash}
}
应该是 json.Marshal(data)
。
在代码的后面,你解析的是字节,但内容是来自结构体的字节,而不是 JSON 文本。
func ListContracts() ([]Contract, error) {
bc := blockchain.NewBlockchain()
contracts := make([]Contract, 0)
blocks := bc.Blocks
for i := len(blocks) - 1; i >= 0; i-- {
block := blocks[i]
var contract blockchain.Contract
// 错误发生在这里
err := json.Unmarshal(block.Data, &contract)
if err != nil {
return nil, err
}
contracts = append(contracts, Contract{contract})
}
return contracts, nil
}
然后你会得到错误。
话虽如此,你应该重新审查你的方法,因为以那种方式进行 JSON 文本的处理似乎是不必要的,但暂时更改应该可以解决问题。
如果你想从那里进行测试,我已经向你的仓库发送了此 PR。
英文:
The error is happening because on blockchain.go#line22 you're setting the struct bytes, not a JSON text.
func CreateBlock(index int, data Contract, prevHash string) *Block {
timestamp := time.Now()
// This is struct bytes, change to 'json.Marshal(data)'
dataBytes := data.ToBytes()
hash := CalculateHash(data, prevHash, timestamp)
return &Block{index, timestamp.Format(time.RFC3339), dataBytes, hash, prevHash}
}
It should be json.Marshal(data)
.
Later in the code you're parsing bytes, but the content is bytes from a struct, not JSON text.
func ListContracts() ([]Contract, error) {
bc := blockchain.NewBlockchain()
contracts := make([]Contract, 0)
blocks := bc.Blocks
for i := len(blocks) - 1; i >= 0; i-- {
block := blocks[i]
var contract blockchain.Contract
// Error happens here
err := json.Unmarshal(block.Data, &contract)
if err != nil {
return nil, err
}
contracts = append(contracts, Contract{contract})
}
return contracts, nil
}
Then you get the error.
That being said, you should review your approach as carrying out the JSON text in that manner seems unnecessary, but changing that for now should work.
I've sent this PR to your repo if you want to test it from there.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论