大家好秉氧,我叫謝偉黑竞,是一名程序員。
下面結(jié)合我的經(jīng)歷和見(jiàn)聞庙楚,講述下一名非科班程序員的成長(zhǎng)過(guò)程:
- 學(xué)習(xí)一門(mén)編程語(yǔ)言
- 寫(xiě)盡量多的代碼
- 補(bǔ)盡量多的基礎(chǔ)知識(shí)
- 一定階段后(有開(kāi)發(fā)任務(wù)上荡,能按時(shí)完成)趴樱,開(kāi)始思考架構(gòu):即如何更好的設(shè)計(jì)一個(gè)項(xiàng)目
- 閱讀源代碼,看熱門(mén)的項(xiàng)目的源代碼
- 重點(diǎn)梳理源代碼的流程而不是細(xì)節(jié)
- 借鑒好的源代碼的思路編寫(xiě)程序
- 掌握更多的軟件設(shè)計(jì)知識(shí)
- 架構(gòu)師:技術(shù)選型酪捡、設(shè)計(jì)
- ...
一般初學(xué)者確定一個(gè)方向叁征,比如web 后端、前端等逛薇,會(huì)選擇一門(mén)編程語(yǔ)言深入下去捺疼,比如后端java、python永罚、go等啤呼。通過(guò)項(xiàng)目不斷練習(xí)編程語(yǔ)言和編程思維,知道如何將需求實(shí)現(xiàn)出來(lái)呢袱。一段時(shí)間后官扣,有可能算是某一階段的瓶頸,希望寫(xiě)出更好的代碼羞福,除了繼續(xù)做項(xiàng)目之外惕蹄,更好的方式是閱讀某一個(gè)庫(kù)或者某一項(xiàng)目的源代碼,從源代碼里學(xué)習(xí)一些編程的處理方式,之后借鑒到自己的項(xiàng)目中卖陵。突破瓶頸遭顶,繼續(xù)精進(jìn)技能。
一般的軟件構(gòu)建過(guò)程是這樣的:
- 設(shè)計(jì):方案確定
- 編寫(xiě)代碼
- 編碼風(fēng)格
- 技術(shù)選型
- 包
- 類(lèi)
- 子程序
- 語(yǔ)句
- 測(cè)試
- 聯(lián)調(diào)
- 迭代:繼續(xù)改善代碼
本節(jié)的主題是:如何閱讀源代碼泪蔫?
1. 明確你的問(wèn)題
開(kāi)源領(lǐng)域棒旗,值得學(xué)習(xí)的東西太多了,你應(yīng)該明確知道你需要解決的問(wèn)題是什么鸥滨,才能針對(duì)性的對(duì)某一項(xiàng)目或者某一庫(kù)進(jìn)行源代碼的閱讀嗦哆。
2. 示例
go-restful
是用于構(gòu)建REST-style web
服務(wù)的golang
包。
在這之前我們需要了解下 HTTP
協(xié)議婿滓、Web 客戶(hù)端老速、服務(wù)端。
這些知識(shí)和我們?cè)L問(wèn)網(wǎng)址獲取到的信息息息相關(guān)凸主。
我們?cè)跒g覽器中輸入:URL
(www.baidu.com)的整體過(guò)程如下:
- 瀏覽器(客戶(hù)端)請(qǐng)求DNS(域名管理系統(tǒng))橘券,獲取IP
- IP 能夠找到對(duì)應(yīng)的服務(wù)器
- 建立TCP 服務(wù)
- 服務(wù)器根據(jù)請(qǐng)求處理請(qǐng)求包(HTTP Request)
- 服務(wù)器返回HTTP Response
- 瀏覽器(客戶(hù)端)收到響應(yīng)后渲染Response 包里的主體(body)
- 斷開(kāi)連接,瀏覽器顯示網(wǎng)頁(yè)信息
我們關(guān)注里面的:HTTP Request
和 HTTP Response
隨意找個(gè)網(wǎng)頁(yè)查看源代碼看看:
HTTP 協(xié)議:HTTP Request
GET /u/58f0817209aa HTTP/1.1
Host: www.reibang.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://www.reibang.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
主要包括:
- 請(qǐng)求行: 請(qǐng)求方法卿吐、請(qǐng)求URI旁舰、HTTP 協(xié)議、協(xié)議版本
- 服務(wù)端信息: Host嗡官、...
- 消息體
HTTP 協(xié)議 HTTP Response
HTTP/1.1 200 OK
Date: Sun, 20 May 2018 03:19:36 GMT
Server: Tengine
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval' *.jianshu.com *.jianshu.io api.geetest.com static.geetest.com dn-staticdown.qbox.me zz.bdstatic.com *.google-analytics.com # push.zhanzhang.baidu.com res.wx.qq.com qzonestyle.gtimg.cn as.alipayobjects.com ;style-src 'self' 'unsafe-inline' *.jianshu.com *.jianshu.io api.geetest.com static.geetest.com ;
ETag: W/"4d22fb2fcef7cdb3f874a6b4960ff2ae"
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: locale=zh-CN; path=/
Set-Cookie: _m7e_session=708ecf714930ebc19da67ae3141bd6c0; path=/; expires=Sun, 20 May 2018 09:19:36 -0000; secure; HttpOnly
X-Request-Id: c61a268c-896f-4e03-afbe-2547db04943d
X-Runtime: 0.137573
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content-Encoding: gzip
X-Via: 1.1 PSfjfzdx2wn96:6 (Cdn Cache Server V2.0), 1.1 jsyz89:1 (Cdn Cache Server V2.0)
Connection: keep-alive
X-Dscp-Value: 0
主要包括:
- 狀態(tài)行:HTTP 協(xié)議箭窜、HTTP 協(xié)議版本、狀態(tài)碼
- 服務(wù)端信息
- 消息體
所以關(guān)于設(shè)計(jì) restful api 的主體部分包括這些:
- HTTP 方法:GET衍腥、POST磺樱、PUT、DELETE
- HTTP Request:URI 路徑婆咸、路徑參數(shù)竹捉、請(qǐng)求參數(shù)
- HTTP Response:狀態(tài)碼(2XX、3XX尚骄、4XX块差、5XX)、消息體(body)
鑒于上面的知識(shí)點(diǎn)倔丈,我們?nèi)绻褂脙?nèi)置的golang 包憨闰,處理 http 信息會(huì)這么做:
func Downloader(url string) ([]byte, error) {
var (
req *http.Request
err error
)
if req, err = http.NewRequest("GET", url, nil); err != nil {
return nil, ErrorHttpRequest
}
client := http.DefaultClient
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36")
var (
resp *http.Response
)
if resp, err = client.Do(req); err != nil {
return nil, ErrorHttpResponse
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
查看下源代碼 net/http 庫(kù)中的 http.Request 和 http.Response 都有些什么?
type Request struct {
// Method specifies the HTTP method (GET, POST, PUT, etc.).
// For client requests an empty string means GET.
Method string
// URL specifies either the URI being requested (for server
// requests) or the URL to access (for client requests).
//
// For server requests the URL is parsed from the URI
// supplied on the Request-Line as stored in RequestURI. For
// most requests, fields other than Path and RawQuery will be
// empty. (See RFC 2616, Section 5.1.2)
//
// For client requests, the URL's Host specifies the server to
// connect to, while the Request's Host field optionally
// specifies the Host header value to send in the HTTP
// request.
URL *url.URL
// The protocol version for incoming server requests.
//
// For client requests these fields are ignored. The HTTP
// client code always uses either HTTP/1.1 or HTTP/2.
// See the docs on Transport for details.
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
// Header contains the request header fields either received
// by the server or to be sent by the client.
//
// If a server received a request with header lines,
//
// Host: example.com
// accept-encoding: gzip, deflate
// Accept-Language: en-us
// fOO: Bar
// foo: two
//
// then
//
// Header = map[string][]string{
// "Accept-Encoding": {"gzip, deflate"},
// "Accept-Language": {"en-us"},
// "Foo": {"Bar", "two"},
// }
//
// For incoming requests, the Host header is promoted to the
// Request.Host field and removed from the Header map.
//
// HTTP defines that header names are case-insensitive. The
// request parser implements this by using CanonicalHeaderKey,
// making the first character and any characters following a
// hyphen uppercase and the rest lowercase.
//
// For client requests, certain headers such as Content-Length
// and Connection are automatically written when needed and
// values in Header may be ignored. See the documentation
// for the Request.Write method.
Header Header
// Body is the request's body.
//
// For client requests a nil body means the request has no
// body, such as a GET request. The HTTP Client's Transport
// is responsible for calling the Close method.
//
// For server requests the Request Body is always non-nil
// but will return EOF immediately when no body is present.
// The Server will close the request body. The ServeHTTP
// Handler does not need to.
Body io.ReadCloser
// GetBody defines an optional func to return a new copy of
// Body. It is used for client requests when a redirect requires
// reading the body more than once. Use of GetBody still
// requires setting Body.
//
// For server requests it is unused.
GetBody func() (io.ReadCloser, error)
// ContentLength records the length of the associated content.
// The value -1 indicates that the length is unknown.
// Values >= 0 indicate that the given number of bytes may
// be read from Body.
// For client requests, a value of 0 with a non-nil Body is
// also treated as unknown.
ContentLength int64
// TransferEncoding lists the transfer encodings from outermost to
// innermost. An empty list denotes the "identity" encoding.
// TransferEncoding can usually be ignored; chunked encoding is
// automatically added and removed as necessary when sending and
// receiving requests.
TransferEncoding []string
// Close indicates whether to close the connection after
// replying to this request (for servers) or after sending this
// request and reading its response (for clients).
//
// For server requests, the HTTP server handles this automatically
// and this field is not needed by Handlers.
//
// For client requests, setting this field prevents re-use of
// TCP connections between requests to the same hosts, as if
// Transport.DisableKeepAlives were set.
Close bool
// For server requests Host specifies the host on which the
// URL is sought. Per RFC 2616, this is either the value of
// the "Host" header or the host name given in the URL itself.
// It may be of the form "host:port". For international domain
// names, Host may be in Punycode or Unicode form. Use
// golang.org/x/net/idna to convert it to either format if
// needed.
//
// For client requests Host optionally overrides the Host
// header to send. If empty, the Request.Write method uses
// the value of URL.Host. Host may contain an international
// domain name.
Host string
// Form contains the parsed form data, including both the URL
// field's query parameters and the POST or PUT form data.
// This field is only available after ParseForm is called.
// The HTTP client ignores Form and uses Body instead.
Form url.Values
// PostForm contains the parsed form data from POST, PATCH,
// or PUT body parameters.
//
// This field is only available after ParseForm is called.
// The HTTP client ignores PostForm and uses Body instead.
PostForm url.Values
// MultipartForm is the parsed multipart form, including file uploads.
// This field is only available after ParseMultipartForm is called.
// The HTTP client ignores MultipartForm and uses Body instead.
MultipartForm *multipart.Form
// Trailer specifies additional headers that are sent after the request
// body.
//
// For server requests the Trailer map initially contains only the
// trailer keys, with nil values. (The client declares which trailers it
// will later send.) While the handler is reading from Body, it must
// not reference Trailer. After reading from Body returns EOF, Trailer
// can be read again and will contain non-nil values, if they were sent
// by the client.
//
// For client requests Trailer must be initialized to a map containing
// the trailer keys to later send. The values may be nil or their final
// values. The ContentLength must be 0 or -1, to send a chunked request.
// After the HTTP request is sent the map values can be updated while
// the request body is read. Once the body returns EOF, the caller must
// not mutate Trailer.
//
// Few HTTP clients, servers, or proxies support HTTP trailers.
Trailer Header
// RemoteAddr allows HTTP servers and other software to record
// the network address that sent the request, usually for
// logging. This field is not filled in by ReadRequest and
// has no defined format. The HTTP server in this package
// sets RemoteAddr to an "IP:port" address before invoking a
// handler.
// This field is ignored by the HTTP client.
RemoteAddr string
// RequestURI is the unmodified Request-URI of the
// Request-Line (RFC 2616, Section 5.1) as sent by the client
// to a server. Usually the URL field should be used instead.
// It is an error to set this field in an HTTP client request.
RequestURI string
// TLS allows HTTP servers and other software to record
// information about the TLS connection on which the request
// was received. This field is not filled in by ReadRequest.
// The HTTP server in this package sets the field for
// TLS-enabled connections before invoking a handler;
// otherwise it leaves the field nil.
// This field is ignored by the HTTP client.
TLS *tls.ConnectionState
// Cancel is an optional channel whose closure indicates that the client
// request should be regarded as canceled. Not all implementations of
// RoundTripper may support Cancel.
//
// For server requests, this field is not applicable.
//
// Deprecated: Use the Context and WithContext methods
// instead. If a Request's Cancel field and context are both
// set, it is undefined whether Cancel is respected.
Cancel <-chan struct{}
// Response is the redirect response which caused this request
// to be created. This field is only populated during client
// redirects.
Response *Response
// ctx is either the client or server context. It should only
// be modified via copying the whole Request using WithContext.
// It is unexported to prevent people from using Context wrong
// and mutating the contexts held by callers of the same request.
ctx context.Context
}
type Response struct {
Status string // e.g. "200 OK"
StatusCode int // e.g. 200
Proto string // e.g. "HTTP/1.0"
ProtoMajor int // e.g. 1
ProtoMinor int // e.g. 0
// Header maps header keys to values. If the response had multiple
// headers with the same key, they may be concatenated, with comma
// delimiters. (Section 4.2 of RFC 2616 requires that multiple headers
// be semantically equivalent to a comma-delimited sequence.) Values
// duplicated by other fields in this struct (e.g., ContentLength) are
// omitted from Header.
//
// Keys in the map are canonicalized (see CanonicalHeaderKey).
Header Header
// Body represents the response body.
//
// The http Client and Transport guarantee that Body is always
// non-nil, even on responses without a body or responses with
// a zero-length body. It is the caller's responsibility to
// close Body. The default HTTP client's Transport does not
// attempt to reuse HTTP/1.0 or HTTP/1.1 TCP connections
// ("keep-alive") unless the Body is read to completion and is
// closed.
//
// The Body is automatically dechunked if the server replied
// with a "chunked" Transfer-Encoding.
Body io.ReadCloser
// ContentLength records the length of the associated content. The
// value -1 indicates that the length is unknown. Unless Request.Method
// is "HEAD", values >= 0 indicate that the given number of bytes may
// be read from Body.
ContentLength int64
// Contains transfer encodings from outer-most to inner-most. Value is
// nil, means that "identity" encoding is used.
TransferEncoding []string
// Close records whether the header directed that the connection be
// closed after reading Body. The value is advice for clients: neither
// ReadResponse nor Response.Write ever closes a connection.
Close bool
// Uncompressed reports whether the response was sent compressed but
// was decompressed by the http package. When true, reading from
// Body yields the uncompressed content instead of the compressed
// content actually set from the server, ContentLength is set to -1,
// and the "Content-Length" and "Content-Encoding" fields are deleted
// from the responseHeader. To get the original response from
// the server, set Transport.DisableCompression to true.
Uncompressed bool
// Trailer maps trailer keys to values in the same
// format as Header.
//
// The Trailer initially contains only nil values, one for
// each key specified in the server's "Trailer" header
// value. Those values are not added to Header.
//
// Trailer must not be accessed concurrently with Read calls
// on the Body.
//
// After Body.Read has returned io.EOF, Trailer will contain
// any trailer values sent by the server.
Trailer Header
// Request is the request that was sent to obtain this Response.
// Request's Body is nil (having already been consumed).
// This is only populated for Client requests.
Request *Request
// TLS contains information about the TLS connection on which the
// response was received. It is nil for unencrypted responses.
// The pointer is shared between responses and should not be
// modified.
TLS *tls.ConnectionState
}
可以看出這兩個(gè)結(jié)構(gòu)體內(nèi)存在著我們之前分析的那些點(diǎn)需五。
如果只使用內(nèi)置的 net/http 的包如何啟動(dòng)一個(gè)web 服務(wù)鹉动?
package main
import (
"fmt"
"net/http"
)
func Say(resp http.ResponseWriter, req *http.Request) {
req.ParseForm()
fmt.Println(req.URL.Host, "-", req.URL.Path, "-", req.Form)
fmt.Fprintf(resp, "hello world")
}
func main() {
http.HandleFunc("/user/hello", Say)
http.ListenAndServe(":8080", nil)
}
訪問(wèn):localhost:8080/user/hello
返回響應(yīng)值:"hello world"
上文中:URL、和響應(yīng)值response警儒,我們?cè)诖a中進(jìn)行了處理训裆。同樣的我們?cè)L問(wèn)真實(shí)的網(wǎng)址眶根, 比如 https://www.baidu.com
則是百度的服務(wù)器端代碼進(jìn)行了處理。
3. 抄和使用 example
上文中大概知道了構(gòu)建 restful api 相關(guān)的一些 http 協(xié)議的知識(shí)边琉, 和內(nèi)置的庫(kù) net/http 的基本使用方法属百。
但別忘了我們的主題是:閱讀 go-restful 的源代碼。
首先变姨,我們應(yīng)該根據(jù)官方文檔學(xué)會(huì)基本的使用:
package main
import (
"fmt"
"log"
"net/http"
"github.com/emicklei/go-restful"
)
type User struct {
Name string
Age string
ID []int
}
type UserResource struct {
// normally one would use DAO (data access object)
users map[string]User
}
// WebService creates a new service that can handle REST requests for User resources.
func (u UserResource) WebService() *restful.WebService {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well
ws.Route(ws.GET("/").To(u.findAllUsers).
// docs
Doc("get all users").
Writes([]User{}).
Returns(200, "OK", []User{}))
ws.Route(ws.GET("/{user-id}").To(u.findUser).
// docs
Doc("get a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("integer").DefaultValue("1")).
Writes(User{}). // on the response
Returns(200, "OK", User{}).
Returns(404, "Not Found", nil))
return ws
}
// GET http://localhost:8080/users
//
func (u UserResource) findAllUsers(request *restful.Request, response *restful.Response) {
list := []User{}
for _, each := range u.users {
list = append(list, each)
}
response.WriteEntity(list)
}
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
id := request.PathParameter("user-id")
usr := u.users[id]
if len(usr.ID) == 0 {
response.WriteErrorString(http.StatusNotFound, "User could not be found.")
} else {
response.WriteEntity(usr)
}
}
func main() {
type APIServer struct {
Container *restful.Container
}
u := UserResource{map[string]User{}}
u.users["xiewei"] = User{
Name: "xiewei",
Age: "20",
ID: []int{1, 2, 3, 4},
}
apiServer := &APIServer{
Container: restful.DefaultContainer.Add(u.WebService()),
}
log.Printf("start listening on localhost:9990")
log.Fatal(http.ListenAndServe(":9990", apiServer.Container))
}
訪問(wèn):localhost:9990/users
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sun, 20 May 2018 04:21:29 GMT
Content-Length: 92
[
{
"Name": "xiewei",
"Age": "20",
"ID": [
1,
2,
3,
4
]
}
]
訪問(wèn):localhost:9990/users/xiewei
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sun, 20 May 2018 04:21:29 GMT
Content-Length: 92
[
{
"Name": "xiewei",
"Age": "20",
"ID": [
1,
2,
3,
4
]
}
]
訪問(wèn):localhost:9990/users/xiewei2
HTTP/1.1 404 Not Found
Date: Sun, 20 May 2018 04:22:59 GMT
Content-Length: 24
Content-Type: text/plain; charset=utf-8
User could not be found.
通過(guò)這個(gè)簡(jiǎn)單的例子族扰,我們大概能夠使用 go-restful 了。
無(wú)外乎還是操作:http.Request定欧、http.Response渔呵, 上述例子的核心是:findAllUsers
和 findUser
這個(gè)兩個(gè)函數(shù),具體的返回值砍鸠、狀態(tài)碼什么的都是由這兩個(gè)函數(shù)定義扩氢。其他的都是一些路由的定義、定義生產(chǎn)者和消費(fèi)者格式爷辱、啟動(dòng)指定端口的web 服務(wù)录豺。
4. 梳理流程
1. 啟動(dòng)并監(jiān)控指定端口的 http 服務(wù)
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
能看出函數(shù)的入口是:Handler 接口
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
httpServer 包含 container .
log.Fatal(http.ListenAndServe(":9990", apiServer.Container))
一個(gè) Container 包含多個(gè) WebService
type Container struct {
webServicesLock sync.RWMutex
webServices []*WebService
ServeMux *http.ServeMux
isRegisteredOnRoot bool
containerFilters []FilterFunction
doNotRecover bool // default is true
recoverHandleFunc RecoverHandleFunction
serviceErrorHandleFunc ServiceErrorHandleFunction
router RouteSelector // default is a CurlyRouter (RouterJSR311 is a slower alternative)
contentEncodingEnabled bool // default is false
}
container 實(shí)現(xiàn)的了Handler 接口
func (c *Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) {
c.ServeMux.ServeHTTP(httpwriter, httpRequest)
}
一個(gè) webservice 包含多個(gè)Route
type WebService struct {
rootPath string
pathExpr *pathExpression // cached compilation of rootPath as RegExp
routes []Route
produces []string
consumes []string
pathParameters []*Parameter
filters []FilterFunction
documentation string
apiVersion string
typeNameHandleFunc TypeNameHandleFunction
dynamicRoutes bool
// protects 'routes' if dynamic routes are enabled
routesLock sync.RWMutex
}
一個(gè) Route 包含HTTP 協(xié)議協(xié)議相關(guān)的HTTP Request 、HTTP Reponse 饭弓、方法等處理
type Route struct {
Method string
Produces []string
Consumes []string
Path string // webservice root path + described path
Function RouteFunction
Filters []FilterFunction
If []RouteSelectionConditionFunction
// cached values for dispatching
relativePath string
pathParts []string
pathExpr *pathExpression // cached compilation of relativePath as RegExp
// documentation
Doc string
Notes string
Operation string
ParameterDocs []*Parameter
ResponseErrors map[int]ResponseError
ReadSample, WriteSample interface{} // structs that model an example request or response payload
// Extra information used to store custom information about the route.
Metadata map[string]interface{}
// marks a route as deprecated
Deprecated bool
}
具體的處理函數(shù)是:RouteFunction
type RouteFunction func(*Request, *Response)
再回過(guò)來(lái)看一下双饥,我們的代碼是怎么處理的:
- 啟動(dòng)http 服務(wù),指定端口并監(jiān)聽(tīng):需要傳入端口和Handler 接口
log.Fatal(http.ListenAndServe(":9990", apiServer.Container))
- 定義一個(gè) container 弟断,container 類(lèi)實(shí)現(xiàn)了Handler 接口
apiServer := &APIServer{
Container: restful.DefaultContainer.Add(u.WebService()),
}
- container 內(nèi)需要定義一個(gè)或者多個(gè) webservice, 內(nèi)含具體的Route 處理函數(shù) RouteFunction
func (u UserResource) WebService() *restful.WebService {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well
ws.Route(ws.GET("/").To(u.findAllUsers).
// docs
Doc("get all users").
Writes([]User{}).
Returns(200, "OK", []User{}))
ws.Route(ws.GET("/{user-id}").To(u.findUser).
// docs
Doc("get a user").
Param(ws.PathParameter("user-id", "identifier of the user").DataType("integer").DefaultValue("1")).
Writes(User{}). // on the response
Returns(200, "OK", User{}).
Returns(404, "Not Found", nil))
return ws
}
好咏花,上面的大致處理流程我們已經(jīng)梳理清楚。
5. 借鑒使用
- 如何抽象出的客觀實(shí)體:比如Route阀趴、Webservice昏翰、Container
- 如何對(duì)Router、WebService舍咖、Container 定義方法
- 如何對(duì)項(xiàng)目進(jìn)行組織矩父。
- 方法如何進(jìn)行的復(fù)用
內(nèi)置庫(kù)內(nèi)存在很多的接口锉桑,對(duì)接口的實(shí)現(xiàn)排霉,不斷的對(duì)內(nèi)置庫(kù)的擴(kuò)展,有可能就重新發(fā)明了一個(gè)熱門(mén)的輪子民轴。
go-restful 庫(kù)便是對(duì)內(nèi)置庫(kù) net/http 的擴(kuò)展攻柠。
總結(jié):
閱讀源代碼首先你需要明確解決的問(wèn)題是什么,其次你會(huì)使用該項(xiàng)目的Demo 或者多個(gè)示例后裸,然后你需要根據(jù)源代碼梳理源代碼流程瑰钮,最后由抄的過(guò)程轉(zhuǎn)變?yōu)榻梃b使用的過(guò)程。
再會(huì)微驶,希望對(duì)你有所啟發(fā)浪谴,我是謝偉开睡。