素材巴巴 > 程序开发 >

一个基本的 go httpserver

程序开发 2023-09-18 09:41:36

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错误。


标签:

上一篇: H5端多点手势缩放 下一篇:
素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。