一个基本的 go httpserver
packet main
负责调用main()
只负责启动和关闭server
用到的是:
packet app
是真正的服务器应用包
使用了4个packet:
"github.com/devfeel/dotweb"
"github.com/track/blogserver/pkg/config"
"github.com/track/blogserver/pkg/controllers"
"github.com/track/blogserver/pkg/routers"
packet dotweb
是一个webserver框架
https://www.kancloud.cn/devfeel/dotweb/346608
主要提供:
1 ServerFile访问服务器静态文件
2 模板渲染 可以指定模板并且自定义数据
3 路由,可以实现url和handler的匹配,比如:app.HttpServer.GET("/hello", hello)
也可以用GetRouterName获取参数的值
app := dotweb.New()app.HttpServer.GET("/hello/:name", func(ctx dotweb.Context) error{return ctx.WriteString("hello " + ctx.GetRouterName("name"))})
也可以通过Group来定义组路由
import "github.com/devfeel/middleware/accesslog"g := app.HttpServer.Group("/admin")
g.Use(accesslog.Middleware())g.Get("/index", func(ctx dotweb.Context) error {ctx.WriteString("/admin/index")return nil
}
4 Cookie
5 Session
6 http请求
可以通过 FormValue 获取 表单数据
可以通过 QueryString 获取 Get参数
可以通过 PostFormValue 获取 PostForm
可以通过 PostBody 获取 Post Body
可以通过 Bind 绑定一个请求内容体到 go 的结构体
// User
type UserInfo struct {UserName stringSex int
}
// Handler
func(ctx dotweb.Context) (err error) {user := new(User)if err := ctx.Bind(user); err != nil {return err}ctx.WriteString(fmt.Sprint(user))return nil
}
7 HTTP响应
https://www.kancloud.cn/devfeel/dotweb/347467
可以通过 WriteString 发送一个默认200状态码的纯文本响应。
可以通过 WriteStringC 发送一个带有状态码的纯文本响应。
可以通过 WriteJson 用于发送一个默认200状态码的 json 对象。它会将 golang 的对象转换成 json 字符串。
可以通过 WriteJsonC 用于发送一个带有状态码的 json 对象。它会将 golang 的对象转换成 json 字符串。
可以通过 WriteJsonp 用于将golang对象转换成 json 并通过回调以 jsonp 的结构发送。
可以通过 View 发送一个默认200状态码的动态生成 html 内容。
可以通过 ViewC 发送一个带有状态码的动态生成 html 内容。
可以通过 Redirect 提供一个 url 用于重定向。
8 错误处理 & 404 设置
最常用的方法:
SetExceptionHandle 设置自定义异常处理接口
当发生404异常时,会默认使用http.NotFound处理,通过DotWeb.SetNotFoundHandle(handler NotFoundHandle)实现自定义处理逻辑
eg:
//配置自定义error
func (app *App) initError() {ec := controllers.NewErrorController()app.Server.SetNotFoundHandle(ec.NotFound)app.Server.SetExceptionHandle(ec.Internal)app.Server.SetMethodNotAllowedHandle(ec.MethodNotAllowed)
}
error controller的实现:
package controllersimport ("fmt""github.com/devfeel/dotweb""github.com/track/blogserver/pkg/common""net/http"
)type ErrorController struct {
}func NewErrorController() *ErrorController {return &ErrorController{}
}//配置404
func (ec *ErrorController) NotFound(context dotweb.Context) {context.WriteJsonC(http.StatusNotFound, common.ErrNotFound)
}//配置500
func (ec *ErrorController) Internal(context dotweb.Context, err error) {errCust := common.Err{Code: common.ErrInternal.Code,Msg: fmt.Sprintf("%s %s", common.ErrInternal.Msg, err.Error()),}context.WriteJsonC(http.StatusInternalServerError, errCust)
}//配置405
func (ec *ErrorController) MethodNotAllowed(context dotweb.Context) {context.WriteJsonC(http.StatusMethodNotAllowed, common.ErrMethodNotAllow
9 实现文件上传
packet config
用来读取配置
这里用的是只加载一次的方式
可以写成重复热加载的方法:
https://www.cnblogs.com/CraryPrimitiveMan/p/7928647.html
packet routers
主要是通过对dotweb中路由功能的封装实现,并且将
type Router struct {server *dotweb.HttpServergroup dotweb.Group
}
比如要对下面的url进行post:/api/admin/extends
那么可以的实现方式是
func NewApiRouter(server *dotweb.HttpServer) *Router {router := &Router{server: server, group: server.Group("/api")}return router
}func (r *Router) Admin() {admin := r.group.Group("/admin")//增加道具addItem(admin.Group("/extends"))
}func addItem(item dotweb.Group) {ec := controllers.NewExtendController()item.OPTIONS("", ec.Options)//处理增加物品item.POST("", ec.AddItem)
}
packet controllers
用来实现routers里面使用的回调
所以这些packet的关系:
routers 使用controller中的handler
routers 封装dotweb库中的路由功能
app 主要是初始化routers 以及利用config来初始化服务器
controller主要是设置routers调用的handler以及错误处理用到的handler
http包分析 step1:
GET http://localhost:4200/login HTTP/1.1
Host: localhost:4200
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
------
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Accept-Ranges: bytes
Content-Type: text/html; charset=UTF-8
Content-Length: 4399
ETag: W/"112f-j3D5/kzKUW5oLFsDL5TiqPe7By0"
Date: Fri, 18 Sep 2020 02:59:51 GMT
Connection: keep-alive
后面就是内容
step1里面:
获取了完整的html
step2 连续的http请求获取了一些js
step3:
OPTIONS http://127.0.0.1:8888/api/admin/extends HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization
Origin: http://localhost:4200
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-Fetch-Dest: empty
Referer: http://localhost:4200/login
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
----------------------
HTTP/1.1 204 No Content
Access-Control-Allow-Headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With,Sign
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Server: dotweb
Date: Fri, 18 Sep 2020 02:59:55 GMT
step3是为了获得允许的操作
最后我们知道允许的操作是:Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
step4:
POST http://127.0.0.1:8888/api/admin/extends HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
Content-Length: 43
Accept: application/json, text/plain, */*
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzA4ODU3MTUsImlhdCI6MTU3MDc5OTMxNSwidXNlcl9pZCI6MiwidXNlcl9pcCI6IiJ9.PoYjptGI2fDabdqdU5WJOvws8Q9Nk2wXmguvPFnmpS4
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://localhost:4200
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:4200/article
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
orderno=12345&uid=taaa&itemid=891&itemnum=2
-----
HTTP/1.1 200 OK
Access-Control-Allow-Headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With,Sign
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Server: dotweb
Date: Wed, 16 Sep 2020 03:29:10 GMT
Content-Length: 49
{"code":0,"msg":"注册道具成功","data":null}
=============================
干掉Authorization之后的 step4 (没有step3了):
POST http://127.0.0.1:8888/api/admin/extends HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
Content-Length: 43
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://localhost:4200
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:4200/article
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
orderno=12345&uid=taaa&itemid=891&itemnum=2
----
HTTP/1.1 200 OK
Access-Control-Allow-Headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With,Sign
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Server: dotweb
Date: Mon, 21 Sep 2020 04:16:57 GMT
Content-Length: 49
{"code":0,"msg":"注册道具成功","data":null}
一些解释:
1 host:主要是为了应对一个真实的主机托管多个域名,并且request里面只有相对url的情况。
相对url需要有基础url作为基础,基础url可以在html中显示指定,或者别的方法。
2 Connection: keep-alive 请求保持连接打开状态
响应中也必须要有,否则客户端就认为服务器不同意保持打开状态,会在response之后关闭连接。
3 Content-Type 中的内容就是所谓的MIME类型
可以对原始内容进行编码
需要用到的是Content-Encoding首部:
4 关于cors
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
Origin: http://localhost:4200 表示跨域请求的来源
因为访问了两个domain,所以会有cors的问题:
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET
以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST
请求),浏览器必须首先使用 OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
服务端返回的 Access-Control-Allow-Origin: *
表明,该资源可以被任意外域访问。如果服务端仅允许来自 http://foo.example 的访问,该首部字段的内容如下:
Access-Control-Allow-Origin: http://foo.example
发送了一个使用 OPTIONS 方法的“
预检请求”。
OPTIONS 是 HTTP/1.1 协议中定义的方法,用以从服务器获取更多信息。该方法不会对服务器资源产生影响。 预检请求中同时携带了下面两个首部字段:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
POST方法本身属于普通方法,其实不需要使用OPTIONS进行预检
但是如果headers里面有自定义的header,在上面的例子就是:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzA4ODU3MTUsImlhdCI6MTU3MDc5OTMxNSwidXNlcl9pZCI6MiwidXNlcl9pcCI6IiJ9.PoYjptGI2fDabdqdU5WJOvws8Q9Nk2wXmguvPFnmpS4
Authorization的头是在angular里面加入的:
去掉Authorization的部分,就不会再发options请求了
5
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Sec-Fetch-Site 表明了一个请求发起者的来源与目标资源来源之间的关系
6 Referer
从主页上链接到一个朋友那里,他的服务器就能够从HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。
主要是用于 防盗链
7 Accept-Language
告知服务器发送何种语言,其中的q=0.9表示:
8 返回码 200 表示成功
其它重要返回:
203
204 空内容 服务器成功执行请求,但是没有返回信息。
响应报文没有实体部分。
比如上例中的 step3 options的response中就没有body.
206状态码代表服务器已经成功处理了部分GET请求(只有发送GET 方法的request, web服务器才可能返回206),
应用场景:
1. FlashGet, 迅雷或者HTTP下载工具都是使用206状态码来实现断点续传
2. 将以个大文档分解为多个下载段同时下载 比如,在线看视频
实例: 一些流媒体技术比如在线视频,可以边看边下载。 就是使用206来实现的。
3XX 重定向状态码
重定向状态码用来告诉浏览器客户端,它们访问的资源已被移动, Web服务器发送一个重定向状态码和一个可选的Location Header, 告诉客户端新的资源地址在哪。
浏览器客户端会自动用Location中提供的地址,重新发送新的Request。 这个过程对用户来说是透明的。
301和302 非常相似, 一个是永久转移,一个是临时转移。
可以在服务器的nginx上设置301和302
4XX客户端错误状态码
有时客户端会发送一些服务器无法处理的东西,比如格式错误的Request, 或者最常见的是, 请求一个不存在的URL。
比较常见的是:
5XX服务器错误状态码
有时候客户端发送了一条有效Request, Web服务器自身却出错了。 可能是Web服务器运行出错了, 或者网站都挂了。 5XX就是用来描述服务器错误的。我们开发网站的时候,当我们的程序出错了时,就会返回500错误。
标签:
相关文章
-
无相关信息