草庐IT

json - 在 HTTP 响应中处理接口(interface)的最佳方式

coder 2023-06-30 原文

我正在使用以这种方式格式化其响应的 API:

{
  "err": 0,
  "data": **Other json structure**
}

我现在获得响应的方式是将响应放入这样的结构中:

type Response struct {
   Err int        `json:"err"`
   Data interface{} `json:"data"`
}

然后我在收到回复后执行此操作

jsonbytes, _ := json.Marshal(resp.Data)
json.Unmarshal(jsonBytes, &dataStruct)

我只是忽略了这个例子的错误。
当我知道数据应该是什么样子以及它应该是什么类型时,我正在编码和解码对我来说似乎有点奇怪。

是否有我没有看到的更简单的解决方案,或者这是正常的做法?

编辑:我应该提一下,响应对象中的数据属性可能会根据我正在执行的 API 调用而有所不同。

最佳答案

JSON 解码器使用反射来查看它要解码到的类型。将未初始化的 interface{} 作为解码数据的目的地,JSON object 被解码为 map[string]interface{}( example in playground ).

这里有一些想法。

选项A

如果您知道数据类型,则可以为每种类型定义一个新的响应结构。示例:

type FooResponse struct {
  Err  int `json:"err"`
  Data Foo `json:"data"`
}

type Foo struct {
  FooField string `json:"foofield"`
}

type BarResponse struct {
  Err  int `json:"err"`
  Data Bar `json:"data"`
}

type Bar struct {
  BarField string `json:"barfield"`
}

选项B

如果您更喜欢单个 Response 结构而不是每个类型一个,您可以告诉 JSON 解码器避免解码 data 字段,直到稍后通过使用json.RawMessage 数据类型:

package main

import (
  "encoding/json"
  "fmt"
  "log"
)

type Response struct {
  Err  int             `json:"err"`
  Data json.RawMessage `json:"data"`
}

type Foo struct {
  FooField string `json:"foofield"`
}

type Bar struct {
  BarField string `json:"barfield"`
}

func main() {
  fooRespJSON := []byte(`{"data":{"foofield":"foo value"}}`)
  barRespJSON := []byte(`{"data":{"barfield":"bar value"}}`)

  var (
    resp Response
    foo  Foo
    bar  Bar
  )

  // Foo
  if err := json.Unmarshal(fooRespJSON, &resp); err != nil {
    log.Fatal(err)
  }
  if err := json.Unmarshal(resp.Data, &foo); err != nil {
    log.Fatal(err)
  }
  fmt.Println("foo:", foo)

  // Bar
  if err := json.Unmarshal(barRespJSON, &resp); err != nil {
    log.Fatal(err)
  }
  if err := json.Unmarshal(resp.Data, &bar); err != nil {
    log.Fatal(err)
  }
  fmt.Println("bar:", bar)
}

输出:

foo: {foo value}
bar: {bar value}

https://play.golang.org/p/Y7D4uhaC4a8

选项C

正如@mkopriva 在对该问题的评论中指出的那样,第三种选择是使用 interface{} 作为中间数据类型并将其预初始化为已知数据类型。

重点在于中介这个词——当然最好避免传递 interface{} ( Rob Pike's Go Proverbs )。这里的用例是允许使用任何数据类型而不需要多个不同的 Response 类型。避免暴露接口(interface){}的一种方法是完全包装响应,仅暴露数据和错误:

package main

import (
  "encoding/json"
  "fmt"
  "log"
)

type Foo struct {
  FooField string `json:"foofield"`
}

type Bar struct {
  BarField string `json:"barfield"`
}

type Error struct {
  Code int
}

func (e *Error) Error() string {
  return fmt.Sprintf("error code %d", e.Code)
}

func unmarshalResponse(data []byte, v interface{}) error {
  resp := struct {
    Err  int         `json:"err"`
    Data interface{} `json:"data"`
  }{Data: v}

  if err := json.Unmarshal(data, &resp); err != nil {
    return err
  }

  if resp.Err != 0 {
    return &Error{Code: resp.Err}
  }

  return nil
}

func main() {
  fooRespJSON := []byte(`{"data":{"foofield":"foo value"}}`)
  barRespJSON := []byte(`{"data":{"barfield":"bar value"}}`)
  errRespJSON := []byte(`{"err": 123}`)

  // Foo
  var foo Foo
  if err := unmarshalResponse(fooRespJSON, &foo); err != nil {
    log.Fatal(err)
  }
  fmt.Println("foo:", foo)

  // Bar
  var bar Bar
  if err := unmarshalResponse(barRespJSON, &bar); err != nil {
    log.Fatal(err)
  }
  fmt.Println("bar:", bar)

  // Error response
  var v interface{}
  if err := unmarshalResponse(errRespJSON, &v); err != nil {
    log.Fatal(err)
  }
}

输出:

foo: {foo value}
bar: {bar value}
2009/11/10 23:00:00 error code 123

https://play.golang.org/p/5SVfQGwS-Wy

关于json - 在 HTTP 响应中处理接口(interface)的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49611868/

有关json - 在 HTTP 响应中处理接口(interface)的最佳方式的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  5. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  6. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  7. ruby-on-rails - 每次我尝试部署时,我都会得到 - (gcloud.preview.app.deploy) 错误响应 : [4] DEADLINE_EXCEEDED - 2

    我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie

  8. ruby-on-rails - 正确的 Rails 2.1 做事方式 - 2

    question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参

  9. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  10. ruby-on-rails - 如何使用 Rack 接收 JSON 对象 - 2

    我有一个非常简单的RubyRack服务器,例如:app=Proc.newdo|env|req=Rack::Request.new(env).paramspreq.inspect[200,{'Content-Type'=>'text/plain'},['Somebody']]endRack::Handler::Thin.run(app,:Port=>4001,:threaded=>true)每当我使用JSON对象向服务器发送POSTHTTP请求时:{"session":{"accountId":String,"callId":String,"from":Object,"headers":

随机推荐