Go 語言的錯誤訊息處理

文章分享appleboy 发表了文章 • 0 个评论 • 64 次浏览 • 1 天前 • 来自相关话题

本文轉錄自: Go 語言的錯誤訊息處理

每個語言對於錯誤訊息的處理方式都不同,在學習每個語言時... 查看全部

本文轉錄自: Go 語言的錯誤訊息處理


每個語言對於錯誤訊息的處理方式都不同,在學習每個語言時,都要先學會如何在程式內處理錯誤訊息 (Error Handler),而在 Go 語言的錯誤處理是非常簡單,本篇會用簡單的範例教大家 Go 如何處理錯誤訊息。



Go 輸出錯誤訊息


在 Go 語言內有兩種方式讓函示 (function) 可以回傳錯誤訊息,一種是透過 errors 套件或 fmt 套件,先看看 errors 套件使用方式:


package main

import (
"errors"
"fmt"
)

func isEnable(enable bool) (bool, error) {
if enable {
return false, errors.New("You can't enable this setting")
}

return true, nil
}

func main() {
if _, err := isEnable(true); err != nil {
fmt.Println(err.Error())
}
}

請先引入 errors 套件,接著透過 errors.New("message here"),就可以實現 error 錯誤訊息。接著我們打開 errors package 原始碼來看看


package errors

// New returns an error that formats as the given text.
func New(text string) error {
return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
s string
}

func (e *errorString) Error() string {
return e.s
}

可以發現 errors 套件內提供了 New 函示,讓開發者可以直接建立 error 物件,並且實現了 error interface。在 Go 語言有定義 error interface 為:


type error interface {
Error() string
}

只要任何 stuct 有實作 Error() 接口,就可以變成 error 物件。這在下面的自訂錯誤訊息會在提到。除了上面使用 errors 套件外,還可以使用 fmt 套件,將上述程式碼改成:


func isEnable(enable bool) (bool, error) {
if enable {
return false, fmt.Errorf("You can't enable this setting")
}

return true, nil
}

這樣也可以成功輸出錯誤訊息,請深入看 fmt.Errorf


// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
func Errorf(format string, a ...interface{}) error {
return errors.New(Sprintf(format, a...))
}

你可以發現在 fmt 套件內,引用了 errors 套件,所以基本上本質是一樣的。


Go 錯誤訊息測試


在 Go 語言如何測試錯誤訊息,直接引用 testing 套件


package error

import "testing"

func TestIsMyError(t *testing.T) {
ok, err := isEnable(true)

if ok {
t.Fatal("should be false")
}

if err.Error() != "You can't enable this setting" {
t.Fatal("message error")
}
}

另外 Go 語言最常用的測試套件 Testify,可以改寫如下:


package error

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestIsEnable(t *testing.T) {
ok, err := isEnable(true)
assert.False(t, ok)
assert.NotNil(t, err)
assert.Equal(t, "You can't enable this setting", err.Error())
}

Go 自訂錯誤訊息


從上面的例子可以看到,錯誤訊息都是固定的,如果我們要動態改動錯誤訊息,就必須帶變數進去。底下我們來看看如何實現自訂錯誤訊息:


package main

import (
"fmt"
)

// MyError is an error implementation that includes a time and message.
type MyError struct {
Title string
Message string
}

func (e MyError) Error() string {
return fmt.Sprintf("%v: %v", e.Title, e.Message)
}

func main() {
err := MyError{"Error Title 1", "Error Message 1"}
fmt.Println(err)

err = MyError{
Title: "Error Title 2",
Message: "Error Message 2",
}
fmt.Println(err)
}

也可以把錯誤訊息包成 Package 方式


package error

import (
"fmt"
)

// MyError is an error implementation that includes a time and message.
type MyError struct {
Title string
Message string
}

func (e MyError) Error() string {
return fmt.Sprintf("%v: %v", e.Title, e.Message)
}

main.go 就可以直接引用 error 套件


package main

import (
"fmt"

my "github.com/go-training/training/example04/error"
)

func main() {
err := my.MyError{"Error Title 1", "Error Message 1"}
fmt.Println(err)

err = my.MyError{
Title: "Error Title 2",
Message: "Error Message 2",
}
fmt.Println(err)
}

如何測試錯誤訊息是我們自己所定義的呢?請在 error 套件內加入底下測試函示


func IsMyError(err error) bool {
_, ok := err.(MyError)
return ok
}

由於我們實作了 error 接口,只要是 Interface 就可以透過 Type assertion 來判斷此錯誤訊息是否為 MyError


package error

import "testing"

func TestIsMyError(t *testing.T) {
err := MyError{"title", "message"}

ok := IsMyError(err)

if !ok {
t.Fatal("error is not MyError")
}

if err.Error() != "title: message" {
t.Fatal("message error")
}
}

這樣在專案裡就可以實現多個錯誤訊息,寫測試時就可以直接判斷錯誤訊息為哪一種自訂格式。


結論


在 Go 裡面寫錯誤訊息真的很方便又很容易,動態訊息請自定,反之,固定訊息請直接宣告 const 就可以了。在寫套件給大家使用時,大部份都是使用固定訊息,如果是大型專案牽扯到資料庫時,通常會用動態自訂錯誤訊息比較多。


上述程式碼請參考這裡

北京/10年老厂招聘GO高级工程师(营销大数据企业,年内上市)

回复

招聘应聘mikerr 发起了问题 • 2 人关注 • 0 个回复 • 156 次浏览 • 3 天前 • 来自相关话题

beego1.8的文档还没更新么?

回复

开源程序kaixinmao 发起了问题 • 1 人关注 • 0 个回复 • 111 次浏览 • 3 天前 • 来自相关话题

【七牛云】Android OpenGL 开发工程师(上海)

回复

招聘应聘七小牛来招聘 发起了问题 • 1 人关注 • 0 个回复 • 117 次浏览 • 5 天前 • 来自相关话题

【七牛云】Android OpenGL 开发工程师(上海)

招聘应聘七小牛来招聘 发表了文章 • 0 个评论 • 62 次浏览 • 5 天前 • 来自相关话题

岗位职责

  1. 参与 Android 图像处理算法的研究和开发

任职要求

  1. 良好的数据结构和算法以及数学基础, 良好的工程素养以及关于 Paper, IETF RFC 与 O... 查看全部

岗位职责



  1. 参与 Android 图像处理算法的研究和开发


任职要求



  1. 良好的数据结构和算法以及数学基础, 良好的工程素养以及关于 Paper, IETF RFC 与 Open Source 的基本阅读与理解能力

  2. 精通 C/C++/Java 编程语言, 熟悉 JNI 机制, 熟悉 NDK 编程, 熟悉汇编

  3. 熟练掌握 OpenGL ES 绘制方法, 深入理解 OpenGL ES 渲染优化策略, 了解 shader 技术

  4. 精通 OpenGL 开发并熟悉 Android GUI 渲染显示机制, 具备在 Android 系统上做过显示系统移植经验优先

  5. 熟练掌握多线程编程, 善于利用各种资源优化代码

  6. 热爱图形图像技术研发工作, 喜欢创新挑战, 自我驱动能力强, 具备良好的沟通能力和团队协作精神





  • 薪资:13k-26k

  • 工作地点:上海上海浦东新区亮秀路112号Y1座310室

  • 投递邮箱:xiamengru@qiniu.com

Beego框架多点登陆只留最后一个,Session有关问题

回复

有问必答jsharkc 发起了问题 • 1 人关注 • 0 个回复 • 140 次浏览 • 6 天前 • 来自相关话题

GopherChina大会门票多了一张, 转手, 要的联系

回复

线下活动FancyGo 发起了问题 • 1 人关注 • 0 个回复 • 136 次浏览 • 6 天前 • 来自相关话题

orm初探

文章分享elvin5 发表了文章 • 0 个评论 • 116 次浏览 • 6 天前 • 来自相关话题

只是研究一下orm是怎么事情而已矣

package main

import (
    "database/sql"
    "fmt"
    "log"
   ... 			查看全部
					

只是研究一下orm是怎么事情而已矣


package main

import (
"database/sql"
"fmt"
"log"
"reflect"
"strconv"

_ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func init() {
log.SetFlags(log.Lshortfile)
var err error
db, err = sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/test?charset=utf8mb4")
if nil != err {
log.Fatal(err)
}

db.SetMaxOpenConns(150)
db.SetMaxIdleConns(100)
err = db.Ping()
if nil != err {
log.Fatal(err)
}
}

func main() {
res, err := Query()
if nil != err {
log.Println(err)
return
}
var t T2
err = GetOne(res[0], &t)
if nil != err {
log.Println(err)
return
}
log.Printf("%+v",t)
}

type T2 struct {
Id int `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Ct string `json:"ct" db:"ct"`
UpdatedAt string `json:"updated_at" db:"updated_at"`
Bt byte `json:"bt" db:"bt"`
}

func Query() ([]map[string]string, error) {
rows, err := db.Query("SELECT * FROM t2")
if nil != err {
log.Println(err)
return nil, err
}

columns, _ := rows.Columns()
scanArgs := make([]interface{}, len(columns))
values := make([]interface{}, len(columns))
for j := range values {
scanArgs[j] = &values[j]
}

record := make([]map[string]string, 0)
for rows.Next() {
//将行数据保存到record字典
err = rows.Scan(scanArgs...)
m := map[string]string{}
for i, col := range values {
if col != nil {
m[columns[i]] = string(col.([]byte))
}
}

record = append(record, m)
}

fmt.Printf("%+v\n", record)

return record, nil
}

func GetOne(m map[string]string, v interface{}) error {
tp := reflect.TypeOf(v)
val := reflect.ValueOf(v)

length := tp.Elem().NumField()
for i := 0; i < length; i++ {
dbTag := tp.Elem().Field(i).Tag.Get("db")
if "" == dbTag || "-" == dbTag {
continue
}

val2, ok := m[dbTag]
if !ok {
continue
}

switch tp.Elem().Field(i).Type.Kind() {
case reflect.String:
val.Elem().Field(i).SetString(val2)
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16:
val3, err := strconv.ParseInt(val2, 10, 64)
if nil != err {
log.Println(err)
continue
}
val.Elem().Field(i).SetInt(val3)
case reflect.Float32, reflect.Float64:
case reflect.Bool:
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
default:
}
}

return nil
}

Go之旅-Switch

文章分享frankphper 发表了文章 • 0 个评论 • 115 次浏览 • 2017-03-15 22:51 • 来自相关话题

Go之旅-Switch

switch支持初始化语句,注意要用分号结束。后跟条件表达式,如果省略条件表达式,默认为true。不需要显示执行break语句,case执行完毕后自动终端。多个匹配条件,其中一个条件符合即可。case执行中断后,... 查看全部

Go之旅-Switch


switch支持初始化语句,注意要用分号结束。后跟条件表达式,如果省略条件表达式,默认为true。不需要显示执行break语句,case执行完毕后自动终端。多个匹配条件,其中一个条件符合即可。case执行中断后,如果需要继续执行下一个case块的内容,在下一个case块结尾执行fallthrough并且可以在fallthrough前使用break语句阻止。但不继续继续后续case块。


package main

import (
"fmt"
)

func main() {
// 简单声明几个变量
a, b, c, d := 1, 2, 3, 4
switch x := 2; x { // switch支持初始化语句,注意要用分号结束。后跟条件表达式,如果省略条件表达式,默认为true。
case a:
fmt.Println("a")
// break // 不需要显示执行break语句,case执行完毕后自动终端。
case a, b: // 多个匹配条件,其中一个条件符合即可。
fmt.Println("b")
fallthrough // case执行中断后,如果需要继续执行下一个case块的内容,在下一个case块结尾执行fallthrough并且可以在fallthrough前使用break语句阻止。但不继续继续后续case块。
case c:
fmt.Println("c")
case d:
fmt.Println("d")
case 5:
fmt.Println("e")
//case 5, 6: // 支持常量,但不能出现重复常量
// fmt.Println("f")
default:
fmt.Println("x") // 只有全部匹配失败后,才会执行default块。
}
}

redis pool 设置多少合适

回复

有问必答kakashi 发起了问题 • 3 人关注 • 0 个回复 • 179 次浏览 • 2017-03-14 15:19 • 来自相关话题

Gitea 发布 v1.1 版本,支持Git-LFS,两步验证,MSSQL,Github登录等大量改进

开源程序lunny 发表了文章 • 0 个评论 • 156 次浏览 • 2017-03-10 10:20 • 来自相关话题

我们很高兴的宣布Gitea 发布了 1.1.0 版本。在这个版本中,我们关闭了 查看全部

我们很高兴的宣布Gitea 发布了 1.1.0 版本。在这个版本中,我们关闭了 126 工单,同时合并了 348 合并请求。你可以从 下载页面 根据你所处的平台和架构下载预编译版本。更多安装详情请参考 安装向导



Changelog



  • BREAKING

    • The SSH keys can potentially break, make sure to regenerate the authorized keys


  • FEATURE

    • Git LFSv2 support #122

    • API endpoints for repo watching #191

    • Search within private repos #222

    • Hide user email address on explore page #336

    • Protected branch system #339

    • Sendmail for mail delivery #355

    • API endpoints for org webhooks #372

    • Enabled MSSQL support #383

    • API endpoints for org teams #370

    • API endpoints for collaborators #375

    • Graceful server restart #416

    • Commitgraph / timeline on commits page #428

    • API endpoints for repo forks #509

    • API endpoints for releases #510

    • Folder jumping #511

    • Stars tab on profile page #519

    • Notification system #523

    • Push and pull through reverse proxy basic auth #524

    • Search for issues and pull requests #530

    • API endpoint for stargazers #597

    • API endpoints for subscribers #598

    • PID file support #610

    • Two factor authentication (2FA) #630

    • API endpoints for org users #645

    • Release attachments #673

    • OAuth2 consumer #679

    • Add ability to fork your own repos #761

    • Search repository on dashboard #773

    • Search bar on user profile #787

    • Track label changes on issue view #788

    • Allow using custom time format #798

    • Redirects for renamed repos #807

    • Track assignee changes on issue view #808

    • Track title changes on issue view #841

    • Archive cleanup action #885

    • Basic Open Graph support #901

    • Take back control of Git hooks #1006

    • API endpoints for user repos #1059


  • BUGFIXES

    • Fixed counting issues for issue filters #413

    • Added back default settings for SSH #500

    • Fixed repo permissions #513

    • Issues cannot be created with labels #622

    • Add a reserved wiki paths check to the wiki #720

    • Update website binding MaxSize to 255 #722

    • User can see the private activity on public history #818

    • Wrong pages number which includes private repositories #844

    • Trim whitespaces for search keyword #893

    • Don't rewrite non-gitea public keys #906

    • Use fingerprint to check instead content for public key #911

    • Fix random avatars #1147


  • ENHANCEMENT

    • Refactored process manager #75

    • Restrict rights to create new orgs #193

    • Added label and milestone sorting #199

    • Make minimum password length configurable #223

    • Speedup conflict checking on pull requests #276

    • Added button to delete merged pull request branches #441

    • Improved issue references within markdown #471

    • Dutch translation for the landingpage #487

    • Added Gogs migration script #532

    • Support a .gitea folder for issue templates #582

    • Enhanced diff-view coloring #584

    • Added ETag header to avatars #721

    • Added option to config to disable local path imports #724

    • Allow custom public files #782

    • Added pprof endpoint for debugging #801

    • Added X-GitHub-* headers #809

    • Fill SSH key title automatically #863

    • Display Git version on admin panel #921

    • Expose URL field on issue API #982

    • Statically compile the binaries #985

    • Embed build tags into version string #1051

    • Gitignore support for FSharp and Clojure #1072

    • Custom templates for static builds #1087

    • Add ProxyFromEnvironment if none set #1096


  • MISC

    • Replaced remaining Gogs references

    • Added more tests on various packages

    • Use Crowdin for translations again

    • Resolved some XSS attack vectors

    • Optimized and reduced number of database queries


【北京 - 三里屯】招聘服务器端资深工程师,目前主PHP副Golang

招聘应聘suilongfei 发表了文章 • 0 个评论 • 206 次浏览 • 2017-03-09 20:25 • 来自相关话题

岗位职责:

1、独立完成功能的设计和开发工作;

2、对APP API系统逐步进行优化和改造;

3、与产品部门进行沟通并完善产品;

4、根据需求进行后台功能的设计和开发;

5、对API项... 查看全部

岗位职责:


1、独立完成功能的设计和开发工作;


2、对APP API系统逐步进行优化和改造;


3、与产品部门进行沟通并完善产品;


4、根据需求进行后台功能的设计和开发;


5、对API项目的整体规划给出自己的意见和建议。


6、参与公司基础服务功能开发


任职要求:


1、计算机、通讯类、数学等相关专业,有5年及以上PHP开发经验;


2、熟练掌握MySQL数据库;


3、熟悉Redis、memcache,Mongodb等数据库;


4、熟悉Linux以及常规Shell操作;


5、有高并发和大数据量处理经验;


6、熟悉golang,有一年以上开发经验以上,加分;


7、有Docker环境工作经验的,加分;


8、善于与他人沟通、合作,具有团队精神,及分析和解决问题的能力,良好的自学能力;


9、能够独立担任开发工作的设计、开发和上线。


薪资范围:20 - 35K/月 + 各种补助 + 月度奖金 + 年终奖等等


有意向者可发送简历至:suilongfei#ifensi.com


公司为明星娱乐方向的互联网公司,工作环境良好,技术团队氛围积极向上

关于scan

回复

有问必答lifesohard 发起了问题 • 1 人关注 • 0 个回复 • 124 次浏览 • 2017-03-08 16:45 • 来自相关话题

beego orm 支持存储过程的调用么?

回复

有问必答andy 发起了问题 • 1 人关注 • 0 个回复 • 145 次浏览 • 2017-03-05 20:15 • 来自相关话题

用docker-machine创建Docker Swarm集群

文章分享myml 发表了文章 • 0 个评论 • 114 次浏览 • 2017-03-02 17:57 • 来自相关话题

参考文档 Install and Create a Docker Swarm


安装


要先安装virtualboxDocker Machine,Docker Machine 是一个简化Docker安装的命令行工具,在非linux系统用docker的同学应该用过。


加速


dockerHub访问比较慢,docker-machine执行create时加上--engine-registry-mirror参数来进行加速,例如docker-machine create -d virtualbox --engine-registry-mirror=https://3cd767jz.mirror.aliyuncs.com local


获取Token


已有docker环境
执行docker run swarm create来从dockerHub获取一个全球唯一的token


没有docker环境
执行docker-machine create -d virtualbox local创建一个docker环境


执行eval $(docker-machine env local) 进入刚创建的local,


再执行docker run swarm create获取token,
很简单吧。machine还有其它一些实用功能,可以自行查看文档


创建master


执行docker-machine create -d virtualbox --swarm --swarm-master --swarm-discovery token://$token swarm-master
$token请替换成上一步骤拿到的token


创建节点


创建节点和创建master类似,只是把--swarm-master参数去掉,名字改下。


执行docker-machine create -d virtualbox --swarm --swarm-discovery token://$token swarm-node-0


再创建一个docker-machine create -d virtualbox --swarm --swarm-discovery token://$token swarm-node-1
执行docker-machine ls可以看到


swarm-master   * (swarm)   virtualbox   Running   tcp://192.168.99.100:2376   swarm-master (master)   v17.03.0-ce   
swarm-node-0 - virtualbox Running tcp://192.168.99.101:2376 swarm-master v17.03.0-ce
swarm-node-0 - virtualbox Running tcp://192.168.99.102:2376 swarm-master v17.03.0-ce

执行eval $(docker-machine env --swarm swarm-master) !注意这里加上了--swarm参数,进入master,执行docker info可以看到集群信息