Go web 教程

GOPHER_AVATARS.jpg

Go Web 新手教程

大家好,我叫謝偉,是一名程序員。

web 應(yīng)用程序是一個各種編程語言一個非常流行的應(yīng)用領(lǐng)域嚎卫。

那么 web 后臺開發(fā)涉及哪些知識呢?

  • 模型設(shè)計(jì):關(guān)系型數(shù)據(jù)庫模型設(shè)計(jì)
  • SQL宏榕、ORM
  • Restful API 設(shè)計(jì)

模型設(shè)計(jì)

web 后臺開發(fā)一般是面向的業(yè)務(wù)開發(fā)拓诸,也就說開發(fā)是存在一個應(yīng)用實(shí)體:比如,面向的是電商領(lǐng)域麻昼,比如面向的是數(shù)據(jù)領(lǐng)域等奠支,比如社交領(lǐng)域等。

不同的領(lǐng)域抚芦,抽象出的模型各不相同倍谜,電商針對的多是商品、商鋪燕垃、訂單枢劝、物流等模型,社交針對的多是人卜壕、消息、群組烙常、帖子等模型轴捎。

盡管市面是的數(shù)據(jù)庫非常繁多鹤盒,不同的應(yīng)用場景選擇不同的數(shù)據(jù)庫,但關(guān)系型數(shù)據(jù)庫依然是中小型企業(yè)的主流選擇侦副,關(guān)系型數(shù)據(jù)庫對數(shù)據(jù)的組織非常友好侦锯。

能夠快速的適用業(yè)務(wù)場景,只有數(shù)據(jù)達(dá)到某個點(diǎn)秦驯,產(chǎn)生某種瓶頸尺碰,比如數(shù)據(jù)量過多,查詢緩慢译隘,這個時候亲桥,會選擇分庫、分表固耘、主從模式等题篷。

數(shù)據(jù)庫模型設(shè)計(jì)依然是一個重要的話題。良好的數(shù)據(jù)模型厅目,為后續(xù)需求的持續(xù)迭代番枚、擴(kuò)展等,非常有幫助损敷。

如何設(shè)計(jì)個良好的數(shù)據(jù)庫模型葫笼?

  • 遵循一些范式:比如著名的數(shù)據(jù)庫設(shè)計(jì)三范式
  • 允許少量冗余

細(xì)講下來,無外乎:1拗馒。 數(shù)據(jù)庫表設(shè)計(jì) 2路星。 數(shù)據(jù)庫字段設(shè)計(jì)、類型設(shè)計(jì) 3瘟忱。 數(shù)據(jù)表關(guān)系設(shè)計(jì):1對1奥额,1對多,多對多

1访诱。 數(shù)據(jù)庫表設(shè)計(jì)

表名
這個沒什么講的垫挨,符合見聞之意的命名即可,但我依然建議触菜,使用 database+實(shí)體的形式九榔。

比如:beeQuick_products 表示:數(shù)據(jù)庫:beeQuick ,表:products

真實(shí)的場景是涡相,設(shè)計(jì)的:生鮮平臺:愛鮮蜂中商品的表

2哲泊。 數(shù)據(jù)庫字段設(shè)計(jì)

字段設(shè)計(jì)、類型設(shè)計(jì)

  • 字段的個數(shù):字段過多催蝗,后期需要進(jìn)行拆表切威;字段過少,會涉及多表操作丙号,所以拿捏尺度很重要先朦,給個指標(biāo):少于12個字段吧缰冤。
  • 如何設(shè)計(jì)字段?: 根據(jù)抽象的實(shí)體喳魏,比如教育系統(tǒng):學(xué)生信息棉浸、老師信息、角色等刺彩,很容易知道表中需要哪些字段迷郑、字段類型。
  • 如果你知道真實(shí)場景创倔,盡量約束字段所占的空間嗡害,比如:電話號碼 11 位,比如:密碼長度 不多于12位

外鍵設(shè)計(jì)

  • 外鍵原本用來維護(hù)數(shù)據(jù)一致性三幻,但真實(shí)使用場景并不會這么用就漾,而是依靠業(yè)務(wù)判斷,比如念搬,將某條記錄的主鍵當(dāng)作某表的某個字段

1對1抑堡,1對多,多對多關(guān)系

  • 1對1: 某表的字段是另一個表的主鍵
type Order struct{
    base
    AccountId  int64
}
  • 1對多:某表的字段是另一個表的主鍵的集合
type Order struct {
    base       `xorm:"extends"`
    ProductIds []int `xorm:"blob"`
    Status     int
    AccountId  int64
    Account    Account `xorm:"-"`
    Total      float64
}
  • 多對多:使用第三張表維護(hù)多對多的關(guān)系
type Shop2Tags struct {
    TagsId int64 `xorm:"index"`
    ShopId int64 `xorm:"index"`
}

ORM

ORM 的思想是對象映射成數(shù)據(jù)庫表朗徊。

在具體的使用中:

1首妖。 根據(jù) ORM 編程語言和數(shù)據(jù)庫數(shù)據(jù)類型的映射,合理定義字段爷恳、字段類型
2有缆。 定義表名稱
3。 數(shù)據(jù)庫表創(chuàng)建温亲、刪除等

在 Go 中比較流行的 ORM 庫是: GORM 和 XORM 棚壁,數(shù)據(jù)庫表的定義等規(guī)則,主要從結(jié)構(gòu)體字段和 Tag 入手栈虚。

字段對應(yīng)數(shù)據(jù)庫表中的列名袖外,Tag 內(nèi)指定類型、約束類型魂务、索引等曼验。如果不定義 Tag, 則采用默認(rèn)的形式。具體的編程語言類型和數(shù)據(jù)庫內(nèi)的對應(yīng)關(guān)系粘姜,需要查看具體的 ORM 文檔鬓照。

// XORM
type Account struct {
    base     `xorm:"extends"`
    Phone    string    `xorm:"varchar(11) notnull unique 'phone'" json:"phone"`
    Password string    `xorm:"varchar(128)" json:"password"`
    Token    string    `xorm:"varchar(128) 'token'" json:"token"`
    Avatar   string    `xorm:"varchar(128) 'avatar'" json:"avatar"`
    Gender   string    `xorm:"varchar(1) 'gender'" json:"gender"`
    Birthday time.Time `json:"birthday"`

    Points      int       `json:"points"`
    VipMemberID uint      `xorm:"index"`
    VipMember   VipMember `xorm:"-"`
    VipTime     time.Time `json:"vip_time"`
}

// GORM
type Account struct {
    gorm.Model
    LevelID  uint
    Phone    string    `gorm:"type:varchar" json:"phone"`
    Avatar   string    `gorm:"type:varchar" json:"avatar"`
    Name     string    `gorm:"type:varchar" json:"name"`
    Gender   int       `gorm:"type:integer" json:"gender"` // 0 男 1 女
    Birthday time.Time `gorm:"type:timestamp with time zone" json:"birthday"`
    Points   sql.NullFloat64
}

另一個具體的操作是: 完成數(shù)據(jù)庫的增刪改查,具體的思想孤紧,仍然是操作結(jié)構(gòu)體對象豺裆,完成數(shù)據(jù)庫 SQL 操作。

當(dāng)然對應(yīng)每個模型的設(shè)計(jì)号显,我一般都會定義一個序列化結(jié)構(gòu)體留储,真實(shí)模型的序列化方法是返回這個定義的序列化結(jié)構(gòu)體翼抠。

具體來說:

// 定義一個具體的序列化結(jié)構(gòu)體咙轩,注意名稱的命名获讳,一致性
type AccountSerializer struct {
    ID        uint                `json:"id"`
    CreatedAt time.Time           `json:"created_at"`
    UpdatedAt time.Time           `json:"updated_at"`
    Phone     string              `json:"phone"`
    Password  string              `json:"-"`
    Token     string              `json:"token"`
    Avatar    string              `json:"avatar"`
    Gender    string              `json:"gender"`
    Age       int                 `json:"age"`
    Points    int                 `json:"points"`
    VipMember VipMemberSerializer `json:"vip_member"`
    VipTime   time.Time           `json:"vip_time"`
}

// 具體的模型的序列化方法返回定義的序列化結(jié)構(gòu)體
func (a Account) Serializer() AccountSerializer {

    gender := func() string {
        if a.Gender == "0" {
            return "男"
        }
        if a.Gender == "1" {
            return "女"
        }
        return a.Gender
    }

    age := func() int {
        if a.Birthday.IsZero() {
            return 0
        }
        nowYear, _, _ := time.Now().Date()
        year, _, _ := a.Birthday.Date()
        if a.Birthday.After(time.Now()) {
            return 0
        }
        return nowYear - year
    }

    return AccountSerializer{
        ID:        a.ID,
        CreatedAt: a.CreatedAt.Truncate(time.Minute),
        UpdatedAt: a.UpdatedAt.Truncate(time.Minute),
        Phone:     a.Phone,
        Password:  a.Password,
        Token:     a.Token,
        Avatar:    a.Avatar,
        Points:    a.Points,
        Age:       age(),
        Gender:    gender(),
        VipTime:   a.VipTime.Truncate(time.Minute),
        VipMember: a.VipMember.Serializer(),
    }
}

項(xiàng)目結(jié)構(gòu)設(shè)計(jì)

├── cmd
├── configs
├── deployments
├── model
│   ├── v1
│   └── v2
├── pkg
│   ├── database.v1
│   ├── error.v1
│   ├── log.v1
│   ├── middleware
│   └── router.v1
├── src
│   ├── account
│   ├── activity
│   ├── brand
│   ├── exchange_coupons
│   ├── make_param
│   ├── make_response
│   ├── order
│   ├── product
│   ├── province
│   ├── rule
│   ├── shop
│   ├── tags
│   ├── unit
│   └── vip_member
└── main.go
└── Makefile

為什么要進(jìn)行項(xiàng)目結(jié)構(gòu)的組織?就問你個問題:雜亂的屋里活喊,找一件東西快丐膝,還是干凈整齊的屋里,找一件東西快钾菊?

合理的項(xiàng)目組織帅矗,利于項(xiàng)目的擴(kuò)展,滿足多變的需求煞烫,這種模塊化的思維浑此,其實(shí)在編程中也常出現(xiàn),比如將整個系統(tǒng)根據(jù)功能劃分滞详。

  • cmd 用于 命令行
  • configs 用于配置文件
  • deployments 部署腳本凛俱,Dockerfile
  • model 用于模型設(shè)計(jì)
  • pkg 用于輔助的庫
  • src 核心邏輯層,這一層料饥,我的一般組織方式為:按模型設(shè)計(jì)的實(shí)體劃分不同的文件夾蒲犬,比如上文賬戶、活動岸啡、品牌原叮、優(yōu)惠券等,另外具體的處理邏輯巡蘸,我又這么劃分:
├── assistance.go // 輔助函數(shù)奋隶,如果重復(fù)使用的輔助函數(shù),會提取到 pkg 層悦荒,或者 utils 層
├── controller.go // 核心邏輯處理層
├── param.go // 請求參數(shù)層:包括參數(shù)校驗(yàn)
├── response.go // 響應(yīng)信息
└── router.go // 路由

  • main.go 函數(shù)入口
  • Makefile 項(xiàng)目構(gòu)建

當(dāng)然你也可以參考:https://github.com/golang-standards/project-layout

框架選擇

  • gin
  • iris
  • echo
    ...

主流的隨便選唯欣,問題不大。使用原生的也行逾冬,但你可能需要多寫很多代碼黍聂,比如路由的設(shè)計(jì)、參數(shù)的校驗(yàn):路徑參數(shù)身腻、請求參數(shù)产还、響應(yīng)信息處理等

Restful 風(fēng)格的API開發(fā)

  • 路由設(shè)計(jì)
  • 參數(shù)校驗(yàn)
  • 響應(yīng)信息

路由設(shè)計(jì)

盡管網(wǎng)上存在很多的 Restful 風(fēng)格的 API 設(shè)計(jì)準(zhǔn)則,但我依然推薦你看看下文的介紹嘀趟。

域名(主機(jī))

推薦使用專有的 API 域名下脐区,比如:https://api.example.com

但實(shí)際上直接放在主機(jī)下:https://example.com/api

版本

需求會不斷的變更,接口也會在不斷的變更她按,所以牛隅,最好給 API 帶上版本:比如:https://example.com/api/v1炕柔,表示 第一個版本。

有些會在頭部信息里帶版本信息媒佣,不推薦匕累,不直觀。

方式這么些默伍,但一定要統(tǒng)一欢嘿。在頭部信息里帶版本信息,那么就一直這樣也糊。如果在路路徑內(nèi)炼蹦,就一致在路徑內(nèi),統(tǒng)一非常重要狸剃。

請求方法

  • POST: 在服務(wù)器上創(chuàng)建資源掐隐,對應(yīng)數(shù)據(jù)庫操作是:create
  • PATCH: 在服務(wù)器上更新資源,對應(yīng)的數(shù)據(jù)庫操作是:update
  • DELETE: 在服務(wù)器上刪除資源钞馁,對應(yīng)的數(shù)據(jù)庫操作是:delete
  • GET: 在服務(wù)器上獲取資源虑省,對應(yīng)的數(shù)據(jù)庫操作是:select
  • 其他:不常用

路由設(shè)計(jì)

整體推薦:版本 + 實(shí)體(名詞) 的形式:

舉個例子:上文的項(xiàng)目結(jié)構(gòu)中的 order 表示的是訂單實(shí)體。

那么路由如何設(shè)計(jì)指攒?

POST /api/v1/order
PATCH /api/v1/order/{order_id:int}
DELETE /api/v1/order/{order_id:int}
GET /api/v1/orders

盡管還存在其他方式慷妙,但我依然推薦需要保持一致性。

比如活動接口:

POST /api/v1/activity
PATCH /api/v1/activity/{activity_id:int}
DELETE /api/v1/activity/{activity_id:int}
GET /api/v1/activities

保持一致性允悦。

參數(shù)校驗(yàn)

路由設(shè)計(jì)中涉及的一個重要的知識點(diǎn)是:參數(shù)校驗(yàn)

  • 比如參數(shù)類型校驗(yàn)
  • 比如參數(shù)長度校驗(yàn)
  • 比如指定選項(xiàng)校驗(yàn)

上文項(xiàng)目示例每個實(shí)體的接口具體的項(xiàng)目結(jié)構(gòu)如下:

├── assistance.go
├── controller.go
├── param.go
├── response.go
└── router.go
  • param.go 核心的就是組織接口中參數(shù)的定義膝擂、參數(shù)的校驗(yàn)

參數(shù)校驗(yàn)有兩種方式:1: 使用結(jié)構(gòu)體方法實(shí)現(xiàn)校驗(yàn)邏輯;2: 使用結(jié)構(gòu)體中的 Tag 定義校驗(yàn)隙弛。

type RegisterParam struct {
    Phone    string `json:"phone"`
    Password string `json:"password"`
}

func (param RegisterParam) suitable() (bool, error) {
    if param.Password == "" || len(param.Phone) != 11 {
        return false, fmt.Errorf("password should not be nil or the length of phone is not 11")
    }
    if unicode.IsNumber(rune(param.Password[0])) {
        return false, fmt.Errorf("password should start with number")
    }
    return true, nil
}

像這種方式架馋,自定義參數(shù)結(jié)構(gòu)體,結(jié)構(gòu)體方法來進(jìn)行參數(shù)的校驗(yàn)全闷。

缺點(diǎn)是:需要寫很多的代碼叉寂,要考慮很多的場景。

另外一種方式是:使用 結(jié)構(gòu)體的 Tag 來實(shí)現(xiàn)总珠。

type RegisterParam struct {
    Phone    string `form:"phone" json:"phone" validate:"required,len=11"`
    Password string `form:"password" json:"password"`
}

func (r RegisterParam) Valid() error {
    return validator.New().Struct(r)
}
 

后者使用的是:https://godoc.org/gopkg.in/go-playground/validator.v9 校驗(yàn)庫屏鳍,gin web框架的參數(shù)校驗(yàn)采用的也是這種方案。

覆蓋的場景局服,特別的多钓瞭,使用者只需要關(guān)注結(jié)構(gòu)體內(nèi) Tag 標(biāo)簽的值即可。

  • 對數(shù)值型參數(shù):校驗(yàn)的方向有:1淫奔、 是否為 0 山涡;2、 最大值,最小值(比如翻頁操作鸭丛,每頁的顯示)3竞穷、區(qū)間、大于鳞溉、小于瘾带、等
  • 對字符串型參數(shù):校驗(yàn)的方向有:1、是否為 nil穿挨;2月弛、枚舉或者特定值:eq="a"|eq="b" 等
  • 特定的場景:比如郵箱、顏色科盛、Base64、十六進(jìn)制等

最常用的還是數(shù)值型和字符串型

響應(yīng)信息

前后端分離菜皂,最流行的數(shù)據(jù)交換格式是:json贞绵。盡管支持各種各種的響應(yīng)信息,比如 html恍飘、xml榨崩、string、json 等章母。

構(gòu)建 Restful 風(fēng)格的API母蛛,我只推薦 json,方便前端或者客戶端的開發(fā)人員調(diào)用乳怎。

確定好數(shù)據(jù)交換的格式為 json 之后彩郊,還需要哪些關(guān)注點(diǎn)?

  • 狀態(tài)碼
  • 具體的響應(yīng)信息
{
    "code": 200,
    "data": {
        "id": 1,
        "created_at": "2019-06-19T23:14:11+08:00",
        "updated_at": "2019-06-20T10:40:09+08:00",
        "status": "已付款",
        "phone": "18717711717",
        "account_id": 1,
        "total": 9.6,
        "product_ids": [
            2,
            3
        ]
    }
} 

推薦統(tǒng)一使用上文的格式: code 用來表示狀態(tài)碼蚪缀,data 用來表示具體的響應(yīng)信息秫逝。

如果是存在錯誤,則推薦使用下面這種格式:

{
    "code": 404,
    "detail": "/v1/ordeda",
    "error": "no route /v1/orderda"
}

狀態(tài)碼也區(qū)分很多種:

  • 1XX: 接受到請求
  • 2XX: 成功
  • 3XX: 重定向
  • 4XX: 客戶端錯誤
  • 5XX: 服務(wù)端錯誤

根據(jù)具體的場景選擇狀態(tài)碼询枚。

真實(shí)的應(yīng)用是:在 pkg 包下定義一個 err 包违帆,實(shí)現(xiàn) Error 方法。

type ErrorV1 struct {
    Detail  string `json:"detail"`
    Message string `json:"message"`
    Code    int    `json:"code"`
}

type ErrorV1s []ErrorV1

func (e ErrorV1) Error() string {
    return fmt.Sprintf("Detail: %s, Message: %s, Code: %d", e.Detail, e.Message, e.Code)
}

定義一些常用的錯誤信息和錯誤碼:

var (

    // database
    ErrorDatabase       = ErrorV1{Code: 400, Detail: "數(shù)據(jù)庫錯誤", Message: "database error"}
    ErrorRecordNotFound = ErrorV1{Code: 400, Detail: "記錄不存在", Message: "record not found"}

    // body
    ErrorBodyJson   = ErrorV1{Code: 400, Detail: "請求消息體失敗", Message: "read json body fail"}
    ErrorBodyIsNull = ErrorV1{Code: 400, Detail: "參數(shù)為空", Message: "body is null"}
)

其他

  • API 文檔:比較流行的是 swagger 文檔金蜀,文檔是其他開發(fā)人員了解接口的重要途徑刷后,考慮到溝通成本,API 文檔必不可少渊抄。
  • 日志:日志是方便開發(fā)人員查看問題的尝胆,也必不可少,業(yè)務(wù)量不復(fù)雜抒线,日志寫入文件中持久化即可班巩;稍復(fù)雜的場景,可以選擇 ELK
  • Dockerfile: web 應(yīng)用,當(dāng)然非常適合以容易的形式部署在主機(jī)上
  • Makefile: 項(xiàng)目構(gòu)建命令抱慌,包括一些測試逊桦、構(gòu)建、運(yùn)行啟動等

Go web 路線圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抑进,一起剝皮案震驚了整個濱河市强经,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寺渗,老刑警劉巖匿情,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異信殊,居然都是意外死亡炬称,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門涡拘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玲躯,“玉大人,你說我怎么就攤上這事鳄乏□纬担” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵橱野,是天一觀的道長朽缴。 經(jīng)常有香客問我,道長水援,這世上最難降的妖魔是什么密强? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮裹唆,結(jié)果婚禮上誓斥,老公的妹妹穿的比我還像新娘。我一直安慰自己许帐,他們只是感情好劳坑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著成畦,像睡著了一般距芬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上循帐,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天框仔,我揣著相機(jī)與錄音,去河邊找鬼拄养。 笑死离斩,一個胖子當(dāng)著我的面吹牛银舱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播跛梗,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼寻馏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了核偿?” 一聲冷哼從身側(cè)響起诚欠,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漾岳,沒想到半個月后轰绵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尼荆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年左腔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耀找。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡翔悠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出野芒,到底是詐尸還是另有隱情,我是刑警寧澤双炕,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布狞悲,位于F島的核電站,受9級特大地震影響妇斤,放射性物質(zhì)發(fā)生泄漏摇锋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一站超、第九天 我趴在偏房一處隱蔽的房頂上張望荸恕。 院中可真熱鬧,春花似錦死相、人聲如沸融求。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽生宛。三九已至,卻和暖如春肮柜,著一層夾襖步出監(jiān)牢的瞬間陷舅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工审洞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留莱睁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像仰剿,于是被迫代替她去往敵國和親创淡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 去年有段時間得空酥馍,就把谷歌GAE的API權(quán)威指南看了一遍辩昆,收獲頗豐,特別是在自己幾乎獨(dú)立開發(fā)了公司的云數(shù)據(jù)中心之后...
    騎單車的勛爵閱讀 20,534評論 0 41
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 10,974評論 6 13
  • 點(diǎn)擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 13,764評論 0 15
  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當(dāng)在唯一索引所對應(yīng)的列上鍵入重復(fù)值時旨袒,會觸發(fā)此異常汁针。 O...
    我想起個好名字閱讀 5,317評論 0 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,103評論 1 32