我的大部分开发经验来自动态类型语言,如 PHP 和 Javascript。通过在 Golang 中重新创建一些旧的 PHP/Javascript REST API,我已经使用 Golang 练习了大约一个月。我觉得我大部分时间都没有按照 Golang 的方式做事。或者更一般地说,我不习惯使用强类型语言。我觉得我过度使用 map[string]interface{}以及它们的一部分,用于在数据来自 http 请求或作为 json http 输出发送时将数据装箱。那么我想知道的是,我要描述的内容是否违背了 golang 的开发理念?或者,如果我违反了使用强类型语言进行开发的原则?
目前,我用 Golang 重写的 REST APIs 程序流程中,大约 90% 都可以通过这 5 个步骤来描述。
第 1 步 - 接收数据
我从 http.Request.ParseForm() 收到 http 表单数据如 formvals := map[string][]string .有时我会存储需要解码的序列化 JSON 对象,如 jsonUserInfo := json.Unmarshal(formvals["user_information"][0]) /* gives some complex json object */ .
第 2 步 - 验证数据
我在 formvals 上进行验证在 SQL 查询中使用它之前,确保所有数据值都是我所期望的。我将所有内容都视为字符串,然后使用 Regex 来确定字符串格式和业务逻辑是否有效(例如 IsEmail、IsNumeric、IsFloat、IsCASLCompliant、IsEligibleForVoting、IsLibraryCardExpired 等...)。我已经为这些类型的验证编写了自己的正则表达式和自定义函数
第 3 步 - 将数据绑定(bind)到 SQL 查询
我用的是 golang 的 database/sql.DB获取我的表单并将它们绑定(bind)到我的 Query 和 Exec 函数,就像这样 Query("SELECT * FROM tblUser WHERE user_id = ?, user_birthday > ? ",formvals["user_id"][0], jsonUserInfo["birthday"]) .我从不关心我作为要绑定(bind)的参数提供的数据类型,所以它们可能都是字符串。我相信上面步骤中的验证已经确定它们可以用于 SQL 使用。
第 4 步 - 将 SQL 结果绑定(bind)到 []map[string]interface{}{}
我Scan()我的查询结果变成了sqlResult := []map[string]interface{}{}因为我不在乎值类型是 null、strings、float、ints 还是其他类型。所以 sqlResult 的模式可能看起来像:
sqlResult =>
[0] {
"user_id":"1"
"user_name":"Bob Smith"
"age":"45"
"weight":"34.22"
},
[1] {
"user_id":"2"
"user_name":"Jane Do"
"age":nil
"weight":"22.22"
}
EagerLoad("tblAddress", "JOIN ON tblAddress.user_id",&sqlResult)然后填充 sqlResult更多信息类型为[]map[string]interface{}{}这样它看起来像这样:sqlResult =>
[0] {
"user_id":"1"
"user_name":"Bob Smith"
"age":"45"
"weight":"34.22"
"addresses"=>
[0] {
"type":"home"
"address1":"56 Front Street West"
"postal":"L3L3L3"
"lat":"34.3422242"
"lng":"34.5523422"
}
[1] {
"type":"work"
"address1":"5 Kennedy Avenue"
"postal":"L3L3L3"
"lat":"34.3422242"
"lng":"34.5523422"
}
},
[1] {
"user_id":"2"
"user_name":"Jane Do"
"age":nil
"weight":"22.22"
"addresses"=>
[0] {
"type":"home"
"address1":"56 Front Street West"
"postal":"L3L3L3"
"lat":"34.3422242"
"lng":"34.5523422"
}
}
http.ResponseWriter.Write(json.Marshal(sqlResult))并为我的 REST API 输出数据map[string]interface{} .例如,我想使用其他 golang 开发人员会使用的更标准的方法来重构第 2 步。所以我找到了这个 https://godoc.org/gopkg.in/go-playground/validator.v9 ,除了它的所有例子都是 structs 。我还注意到大多数博客都在谈论 database/sql将他们的 SQL 结果扫描到类型化变量或具有类型化属性的结构中,这与我的第 4 步相反,它只是将所有内容放入 map[string]interface{}map[string]interface{}非常有用,因为在大多数情况下,我并不真正关心数据是什么,它让我可以在第 4 步中自由地构建任何数据模式,然后再将其转储为 JSON http 响应。我以尽可能少的代码冗长完成所有这些。但这意味着我的代码还没有准备好利用 Go 的验证工具,而且它似乎不符合 golang 社区的做事方式。最佳答案
这真的取决于需求,就像您所说的那样,您不需要处理来自请求或 sql 结果的 json。然后你可以轻松地解码到 interface{} .并编码来自 sql 结果的 json。
对于 步骤 2
Golang 有一个库,用于验证用于解码 json 的结构体,其中包含字段的标签。
https://github.com/go-playground/validator
type Test struct {
Field `validate:"max=10,min=1"`
}
// max will be checked then min
_ 一起使用json 标签。sql.NullTypes 将结果扫描到结构中.有了它,你也可以为 omitempty 提供 json 标签如果你想在 marshaling 时省略 json 对象发送响应时的数据。Struct values encode as JSON objects. Each exported struct field becomes a member of the object, using the field name as the object key, unless the field is omitted for one of the reasons given below.
The encoding of each struct field can be customized by the format string stored under the "json" key in the struct field's tag. The format string gives the name of the field, possibly followed by a comma-separated list of options. The name may be empty in order to specify options without overriding the default field name.
The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string.
As a special case, if the field tag is "-", the field is always omitted. Note that a field with name "-" can still be generated using the tag "-,".
// Field appears in JSON as key "myName".
Field int `json:"myName"`
// Field appears in JSON as key "myName" and
// the field is omitted from the object if its value is empty,
// as defined above.
Field int `json:"myName,omitempty"`
// Field appears in JSON as key "Field" (the default), but
// the field is skipped if empty.
// Note the leading comma.
Field int `json:",omitempty"`
// Field is ignored by this package.
Field int `json:"-"`
// Field appears in JSON as key "-".
Field int `json:"-,"`
map[string]interface{}当您的 json 结构不是来自服务器或字段类型时,您应该使用它。大多数 Golang 开发人员都尽可能坚持使用结构。
关于json - 在 Go 开发中过度使用 map[string]interface{}?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51581729/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po