<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><title>贝莉卡个人博客</title><link>https://www.beilika.com/</link><description>贝莉卡个人博客，分享JS，HTML，JAVA，PHP，Linux，Golang等编程语言技术</description><language>zh-cn</language><copyright>Copyright beilika.com. All Rights Reserved</copyright><lastBuildDate>Sun, 31 May 2026 15:26:48 +0800</lastBuildDate><item><title><![CDATA[Gin学习-05.gin中间件和路由]]></title><link>https://www.beilika.com/article/c194.html</link><description><![CDATA[路由r.GET()r.POST()r.PUT()r.PATCH()r.DELETE()路由分组把一类api划分到一个组可以给这个组加上统一的中间件RESETFulApi规范尽量使用名称的复数来定义路由//在没有resetful规范正确，表示创建用户，删除用户/api/user_create/api/users/create/api/users/add/api/add_user/api/user/delete/api/user_remove//使用resetful规范GET/api/users用户列表POST/api/users创建用户PUT/api/users/:id更新用户信息DELETE/api/users批量删除用户DELETE/api/users/:id删除单个用户但是resetful它只是一个api规范登陆/api/login大家在公司里面的项目，基本上都是POST请求很早之前的，那个时候还没有RESETFul规范这个说法很多公司的防火墙会拦截除GET和POST之外的请求中间件局部中间件直接作用于单个路由///3.中间件.gopackagemainimport("fmt""github.com/gin-gonic/gin")funcHome(c*gin.Context){fmt.Println("Home")c.String(200,"Home")}funcM1(c*gin.Context){fmt.Println("M1请求部分")c.Next()fmt.Println("M1响应部分")}funcM2(c*gin.Context){fmt.Println("M2请求部分")c.Next()fmt.Println("M2响应部分")}funcmain(){r:=gin.Default()r.GET("",M1,M2,Home)r.Run(":8080")}可以使用Abort进行拦截，使用Abort拦截之后，会原路返回全局中间件全局也就是路由组。这也就是给路由分组的意义///3.中间件.gopackagemainimport("fmt""github.com/gin-gonic/gin")funcHome(c*gin.Context){fmt.Println("Home")c.String(200,"Home")}funcM1(c*gin.Context){fmt.Println("M1请求部分")c.Abort()fmt.Println("M1响应部分")}funcM2(c*gin.Context){fmt.Println("M2请求部分")c.Next()fmt.Println("M2响应部分")}funcGM1(c*gin.Context){fmt.Println("GM1请求部分")c.Next()fmt.Println("GM1响应部分")}funcGM2(c*gin.Context){fmt.Println("GM2请求部分")c.Next()fmt.Println("GM2响应部分")}funcAuthMiddleware(c*gin.Context){}funcmain(){r:=gin.Default()g:=r.Group("api")g.Use(GM1,GM2)g.GET("users",Home)r.Run(":8080")}中间件传递参数c.Set("GM1","GM1")fmt.Println(c.Get("GM1"))]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c194.html</guid><pubDate>Sun, 31 May 2026 15:26:48 +0800</pubDate></item><item><title><![CDATA[Gin学习-04.gin参数绑定]]></title><link>https://www.beilika.com/article/c193.html</link><description><![CDATA[参数绑定使用binding可以很好的完成参数的绑定查询参数typeUserstruct{Namestring`form:"name"`Ageint`form:"age"`}varuserUsererr:=c.ShouldBindQuery(&user)fmt.Println(user,err)路径参数typeUserstruct{Namestring`uri:"name"`IDint`uri:"id"`}varuserUsererr:=c.ShouldBindUri(&user)fmt.Println(user,err)表单参数typeUserstruct{Namestring`form:"name"`Ageint`form:"age"`}varuserUsererr:=c.ShouldBind(&user)fmt.Println(user,err)注意：不能解析x-www-form-urlencoded的格式JSON参数typeUserstruct{Namestring`json:"name"`Ageint`json:"age"`}varuserUsererr:=c.ShouldBindJSON(&user)fmt.Println(user,err)header参数typeUserstruct{Namestring`header:"Name"`Ageint`header:"Age"`UserAgentstring`header:"User-Agent"`ContentTypestring`header:"Content-Type"`}varuserUsererr:=c.ShouldBindHeader(&user)fmt.Println(user,err)内置规则如果有多个规则，使用逗号分隔//不能为空，并且不能没有这个字段required：必填字段，如：binding:"required"//针对字符串的长度min最小长度，如：binding:"min=5"max最大长度，如：binding:"max=10"len长度，如：binding:"len=6"//针对数字的大小eq等于，如：binding:"eq=3"ne不等于，如：binding:"ne=12"gt大于，如：binding:"gt=10"gte大于等于，如：binding:"gte=10"lt小于，如：binding:"lt=10"lte小于等于，如：binding:"lte=10"//针对同级字段的eqfield等于其他字段的值，如：PassWordstring`binding:"eqfield=Password"`nefield不等于其他字段的值-忽略字段，如：binding:"-"或者不写//枚举只能是red或greenoneof=redgreen//字符串contains=fengfeng//包含fengfeng的字符串excludes//不包含startswith//字符串前缀endswith//字符串后缀//数组dive//dive后面的验证就是针对数组中的每一个元素//网络验证ipipv4ipv6uriurl//uri在于I(Identifier)是统一资源标示符，可以唯一标识一个资源。//url在于Locater，是统一资源定位符，提供找到该资源的确切路径//日期验证1月2号下午3点4分5秒在2006年datetime=2006-01-02自定义规则错误信息显示中文packagemainimport("fmt""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""github.com/go-playground/locales/zh""github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"zh_translations"github.com/go-playground/validator/v10/translations/zh""net/http""strings")vartransut.Translatorfuncinit(){//创建翻译器uni:=ut.New(zh.New())trans,_=uni.GetTranslator("zh")//注册翻译器v,ok:=binding.Validator.Engine().(*validator.Validate)ifok{_=zh_translations.RegisterDefaultTranslations(v,trans)}}funcValidateErr(errerror)string{errs,ok:=err.(validator.ValidationErrors)if!ok{returnerr.Error()}varlist[]stringfor_,e:=rangeerrs{list=append(list,e.Translate(trans))}returnstrings.Join(list,";")}typeUserstruct{Namestring`json:"name"binding:"required"`Emailstring`json:"email"binding:"required,email"`}funcmain(){r:=gin.Default()//注册路由r.POST("/user",func(c*gin.Context){varuserUseriferr:=c.ShouldBindJSON(&user);err!=nil{//参数验证失败c.String(200,ValidateErr(err))return}//参数验证成功c.JSON(http.StatusOK,gin.H{"message":fmt.Sprintf("Hello,%s!Youremailis%s.",user.Name,user.Email),})})//启动HTTP服务器r.Run()}字段名也显示中文funcinit(){//创建翻译器uni:=ut.New(zh.New())trans,_=uni.GetTranslator("zh")//注册翻译器v,ok:=binding.Validator.Engine().(*validator.Validate)ifok{_=zh_translations.RegisterDefaultTranslations(v,trans)}v.RegisterTagNameFunc(func(fieldreflect.StructField)string{label:=field.Tag.Get("label")iflabel==""{returnfield.Name}returnlabel})}错误信息和错误字段一起返回packagemainimport("fmt""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""github.com/go-playground/locales/zh""github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"zh_translations"github.com/go-playground/validator/v10/translations/zh""net/http""reflect""strings")vartransut.Translatorfuncinit(){//创建翻译器uni:=ut.New(zh.New())trans,_=uni.GetTranslator("zh")//注册翻译器v,ok:=binding.Validator.Engine().(*validator.Validate)ifok{_=zh_translations.RegisterDefaultTranslations(v,trans)}v.RegisterTagNameFunc(func(fieldreflect.StructField)string{label:=field.Tag.Get("label")iflabel==""{label=field.Name}name:=field.Tag.Get("json")returnfmt.Sprintf("%s---%s",name,label)})}/*{"name":"name参数必填",}*/funcValidateErr(errerror)any{errs,ok:=err.(validator.ValidationErrors)if!ok{returnerr.Error()}varm=map[string]any{}for_,e:=rangeerrs{msg:=e.Translate(trans)_list:=strings.Split(msg,"---")m[_list[0]]=_list[1]}returnm}typeUserstruct{Namestring`json:"name"binding:"required"label:"用户名"`Emailstring`json:"email"binding:"required,email"`}funcmain(){r:=gin.Default()//注册路由r.POST("/user",func(c*gin.Context){varuserUseriferr:=c.ShouldBindJSON(&user);err!=nil{//参数验证失败c.JSON(200,map[string]any{"code":7,"msg":"验证错误","data":ValidateErr(err),})return}//参数验证成功c.JSON(http.StatusOK,gin.H{"message":fmt.Sprintf("Hello,%s!Youremailis%s.",user.Name,user.Email),})})//启动HTTP服务器r.Run()}自定义校验packagemainimport("fmt""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""github.com/go-playground/locales/zh""github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"zh_translations"github.com/go-playground/validator/v10/translations/zh""net""net/http""reflect""strings")vartransut.Translatorfuncinit(){//创建翻译器uni:=ut.New(zh.New())trans,_=uni.GetTranslator("zh")//注册翻译器v,ok:=binding.Validator.Engine().(*validator.Validate)ifok{_=zh_translations.RegisterDefaultTranslations(v,trans)}v.RegisterTagNameFunc(func(fieldreflect.StructField)string{label:=field.Tag.Get("label")iflabel==""{label=field.Name}name:=field.Tag.Get("json")returnfmt.Sprintf("%s---%s",name,label)})v.RegisterValidation("fip",func(flvalidator.FieldLevel)bool{fmt.Println("fl.Field():",fl.Field())fmt.Println("fl.FieldName():",fl.FieldName())fmt.Println("fl.StructFieldName():",fl.StructFieldName())fmt.Println("fl.Parent():",fl.Parent())fmt.Println("fl.Top():",fl.Top())fmt.Println("fl.Param():",fl.Param())ip,ok:=fl.Field().Interface().(string)ifok&&ip!=""{//传了值就去校验是不是IP地址ipObj:=net.ParseIP(ip)returnipObj!=nil}returntrue})}/*{"name":"name参数必填",}*/funcValidateErr(errerror)any{errs,ok:=err.(validator.ValidationErrors)if!ok{returnerr.Error()}varm=map[string]any{}for_,e:=rangeerrs{msg:=e.Translate(trans)_list:=strings.Split(msg,"---")ife.Tag()=="fip"{m[strings.Split(e.Field(),"---")[0]]="该ip地址不符合要求"continue}m[_list[0]]=_list[1]}returnm}funcmain(){r:=gin.Default()//注册路由r.POST("/user",func(c*gin.Context){typeUserstruct{Ipstring`json:"ip"binding:"fip=1234"label:"ip地址"`}varuserUseriferr:=c.ShouldBindJSON(&user);err!=nil{//参数验证失败c.JSON(200,map[string]any{"code":7,"msg":"验证错误","data":ValidateErr(err),})return}c.JSON(http.StatusOK,user)})//启动HTTP服务器r.Run(":8080")}]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c193.html</guid><pubDate>Sun, 31 May 2026 15:24:11 +0800</pubDate></item><item><title><![CDATA[Go基础-16.go文件操作]]></title><link>https://www.beilika.com/article/c185.html</link><description><![CDATA[文件读取一次性读取byteData,_:=os.ReadFile("go_study/hello.txt")fmt.Println(string(byteData))获取当前go文件的路径可以通过获取当前go文件的路径，然后用相对于当前go文件的路径去打开文件//GetCurrentFilePath获取当前文件路径funcGetCurrentFilePath()string{_,file,_,_:=runtime.Caller(1)returnfile}分片读file,_:=os.Open("go_study/hello.txt")deferfile.Close()for{buf:=make([]byte,1)_,err:=file.Read(buf)iferr==io.EOF{break}fmt.Printf("%s",buf)}带缓冲读按行读file,_:=os.Open("go_study/hello.txt")buf:=bufio.NewReader(file)for{line,_,err:=buf.ReadLine()fmt.Println(string(line))iferr!=nil{break}}指定分割符file,_:=os.Open("go_study/hello.txt")scanner:=bufio.NewScanner(file)scanner.Split(bufio.ScanWords)//按照单词读//scanner.Split(bufio.ScanLines)//按照行读//scanner.Split(bufio.ScanRunes)//按照中文字符读//scanner.Split(bufio.ScanBytes)//按照字节读读，中文会乱码forscanner.Scan(){fmt.Println(scanner.Text())}文件写入一次性写err:=os.WriteFile("go_study/file1.txt",[]byte("这是内容"),os.ModePerm)fmt.Println(err)文件的打开方式常见的一些打开模式//如果文件不存在就创建os.O_CREATE|os.O_WRONLY//追加写os.O_APPEND|os.O_WRONLY//可读可写os.O_RDWR完整的模式const(O_RDONLYint=syscall.O_RDONLY//只读O_WRONLYint=syscall.O_WRONLY//只写O_RDWRint=syscall.O_RDWR//读写O_APPENDint=syscall.O_APPEND//追加O_CREATEint=syscall.O_CREAT//如果不存在就创建O_EXCLint=syscall.O_EXCL//文件必须不存在O_SYNCint=syscall.O_SYNC//同步打开O_TRUNCint=syscall.O_TRUNC//打开时清空文件)文件的权限主要用于linux系统，在windows下这个参数会被无视，代表文件的模式和权限位，三个占位符第一个是文件所有者所拥有的权限第二个是文件所在组对其拥有的权限第三个占位符是指其他人对文件拥有的权限根据UNIX系统的权限模型，文件或目录的权限模式由三个数字表示，分别代表所有者（Owner）、组（Group）和其他用户(Other)的权限。每个数字由三个比特位组成，分别代表读、写和执行权限。因此，对于一个mode参数值，它的每一个数字都是一个八进制数字，代表三个比特位的权限组合R：读，Read的缩写，八进制值为4；W：写，Write的缩写，八进制值为2；X：执行，Execute的缩写，八进制值为1；例如0444表示三者均为只读的权限；0666表示三者均为“读写”的权限；0777表示三者均为读写执行的权限；0764表示所有者有读写执行（7=4+2+1）的权限，组有读写（6=4+2）的权限，其他用户则为只读（4=4）；文件复制io.Copy(dstWriter,srcReader)(writtenint64,errerror)将src文件的内容复制到dst文件read,_:=os.Open("go_study/file1.txt")write,_:=os.Create("go_study/file3.txt")//默认是可读可写，不存在就创建，清空文件n,err:=io.Copy(write,read)fmt.Println(n,err)目录操作dir,_:=os.ReadDir("go_study")for_,entry:=rangedir{info,_:=entry.Info()fmt.Println(entry.Name(),info.Size())//文件名，文件大小，单位比特}]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c185.html</guid><pubDate>Sat, 30 May 2026 13:58:43 +0800</pubDate></item><item><title><![CDATA[Gin学习-03.gin请求]]></title><link>https://www.beilika.com/article/c192.html</link><description><![CDATA[查询参数?key=xxx&name=xxxx&name=yyyy这种就被称为查询参数查询参数不是GET请求专属的name:=c.Query("name")age:=c.DefaultQuery("age","25")keyList:=c.QueryArray("key")fmt.Println(name,age,keyList)例如请求?name=fengfeng&age=123&key=123&key=124，输出为：fengfeng123[123124]动态参数用户的个人信息页面，他的路径/users?id=123//查询参数的模式/users/123//动态参数模式r.GET("users/:id",func(c*gin.Context){userID:=c.Param("id")fmt.Println(userID)})表单参数一般就是专指form表单name:=c.PostForm("name")age,ok:=c.GetPostForm("age")fmt.Println(name)fmt.Println(age,ok)文件上传r.POST("users",func(c*gin.Context){fileHeader,err:=c.FormFile("file")iferr!=nil{fmt.Println(err)return}fmt.Println(fileHeader.Filename)//文件名fmt.Println(fileHeader.Size)//文件大小，单位是字节file,_:=fileHeader.Open()byteData,_:=io.ReadAll(file)err=os.WriteFile("xxx.jpg",byteData,0666)fmt.Println(err)})还有一种简单方式err=c.SaveUploadedFile(fileHeader,"uploads/xxx/yyy/"+fileHeader.Filename)fmt.Println(err)多文件上传r.POST("users",func(c*gin.Context){form,err:=c.MultipartForm()iferr!=nil{fmt.Println(err)return}for_,headers:=rangeform.File{for_,header:=rangeheaders{c.SaveUploadedFile(header,"uploads/"+header.Filename)}}})关于接口测试工具postman，apifox注意：接口测试工具能走通的，前端请求不一定可以走通1.get请求带请求体2.ws加请求头原始内容不同的请求体对应的原始内容body阅后即焚问题解决byteData,_:=io.ReadAll(c.Request.Body)fmt.Println(string(byteData))//读了之后，body就没了，阅后即焚c.Request.Body=io.NopCloser(bytes.NewReader(byteData))form-data----------------------------853882779395683818968400Content-Disposition:form-data;name="name"枫枫----------------------------853882779395683818968400Content-Disposition:form-data;name="age"1234----------------------------853882779395683818968400--对应的分隔符就是Content-Type:[multipart/form-data;boundary=--------------------------052455317193517003536866]x-www-form-urlencodedurl编码name=%E6%9E%AB%E6%9E%AB&age=1234json{"name":"枫枫","age":23}]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c192.html</guid><pubDate>Sat, 30 May 2026 13:56:02 +0800</pubDate></item><item><title><![CDATA[Gin学习-02.gin响应]]></title><link>https://www.beilika.com/article/c191.html</link><description><![CDATA[gin提供了非常多的响应方法例如字符串、json、html等json响应现在大部分的前后端交互都是以json为主，所以gin中最常用的就是json响应它的用法非常简单c.JSON(200,gin.H{"code":0,"msg":"ok",})但是我们都会对其进行一番封装，例如标准响应格式code，data，msg前端可以判断code的值来确定操作是否成功不过code的定义就是每家公司都有不同的定义了我比较习惯于code=0为操作成功的状态码，非0值就是具体的错误码，这样可以方便定位错误例如code=1001是权限错误，code=1002是资源不存在等如何封装？packageresimport"github.com/gin-gonic/gin"typeResponsestruct{Codeint`json:"code"`Dataany`json:"data"`Msgstring`json:"msg"`}typeCodeintconst(RoleErrCodeCode=1001NetworkErrCodeCode=1002)varcodeMap=map[Code]string{RoleErrCode:"权限错误",NetworkErrCode:"网络错误",}funcinit(){//可能是一个}funcresponse(c*gin.Context,rResponse){c.JSON(200,r)}funcOk(c*gin.Context,dataany,msgstring){response(c,Response{Code:0,Data:data,Msg:msg,})}funcOkWithData(c*gin.Context,dataany){Ok(c,data,"成功")}funcOkWithMsg(c*gin.Context,msgstring){Ok(c,map[string]any{},msg)}funcFail(c*gin.Context,codeint,dataany,msgstring){response(c,Response{Code:code,Data:data,Msg:msg,})}funcFailWithMsg(c*gin.Context,msgstring){response(c,Response{Code:7,Data:nil,Msg:msg,})}funcFailWithCode(c*gin.Context,codeCode){//去找code对应的msgmsg,ok:=codeMap[code]if!ok{msg="未知错误"}response(c,Response{Code:int(code),Data:nil,Msg:msg,})}封装之后，使用就很简单了res.OkWithMsg(c,"登陆成功")res.OkWithData(c,map[string]any{"name":"枫枫",})res.FailWithMsg(c,"参数错误")html响应使用LoadHTMLGlob加载一个目录下的所有文件也可以使用LoadHTMLFiles加载单个文件load之后，下面才能用这个文件名packagemainimport"github.com/gin-gonic/gin"funcmain(){r:=gin.Default()//加载模板只有这里加载了模板，下面才能用r.LoadHTMLGlob("templates/*")//r.LoadHTMLFiles("templates/index.html")r.GET("",func(c*gin.Context){c.HTML(200,"index.html",nil)})r.Run(":8080")}HTML的第三个参数是可以向HTML中传递数据但是现在都是前后端分离的时代了，也很少使用后端返回模板了，知道怎么用就好c.HTML(200,"index.html",map[string]any{"title":"这是网页标题",})html文件中使用{{.title}}其实借助这个功能，可以很方便的修改网站的标题和logo简单的扩展一下关于部署：前端单独部署，后端单独部署前端打包之后，后端统一部署响应文件用于浏览器直接请求这个接口唤起下载c.Header("Content-Type","application/octet-stream")//表示是文件流，唤起浏览器下载，一般设置了这个，就要设置文件名c.Header("Content-Disposition","attachment;filename=3.文件下载.go")//用来指定下载下来的文件名c.File("3.文件下载.go")要设置Content-Type，唤起浏览器下载只能是get请求前端请求后端接口，然后唤起浏览器下载c.Header("fileName","xxx.png")c.Header("msg","文件下载成功")c.File("uploads/12.png")前端唤起浏览器下载的本质文件下载asyncdownloadFile(row){this.$http({method:'post',url:'file/upload',data:postData,responseType:"blob"}).then(res=>{const_res=res.dataletblob=newBlob([_res],{type:'application/png'});letdownloadElement=document.createElement("a");lethref=window.URL.createObjectURL(blob);//创建下载的链接downloadElement.href=href;downloadElement.download=res.headers["fileName"];//下载后文件名document.body.appendChild(downloadElement);downloadElement.click();//点击下载document.body.removeChild(downloadElement);//下载完成移除元素window.URL.revokeObjectURL(href);//释放掉blob对象})}最好的做法调下载接口的请求，后端不返回实际文件内容，而是生成一个临时下载地址前端构造a标签，再请求这个接口唤起浏览器下载静态文件r.Static("static","static")//第一个参数是别名，第二个才是实际路径r.StaticFile("abcd","static/abc.txt")静态文件的路径，不能再被路由使用了]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c191.html</guid><pubDate>Sat, 30 May 2026 13:48:48 +0800</pubDate></item><item><title><![CDATA[Gin学习-01.go原生http服务]]></title><link>https://www.beilika.com/article/c190.html</link><description><![CDATA[在go中写一个web服务非常方便快速packagemainimport("encoding/json""fmt""io""net/http")typeResponsestruct{Codeint`json:"code"`Dataany`json:"data"`Msgstring`json:"msg"`}funcGET(reshttp.ResponseWriter,req*http.Request){//获取参数fmt.Println(req.URL.String())byteData,_:=json.Marshal(Response{Code:0,Data:map[string]any{},Msg:"成功",})res.Write(byteData)}funcPOST(reshttp.ResponseWriter,req*http.Request){//获取参数byteData,_:=io.ReadAll(req.Body)fmt.Println(string(byteData))byteData,_=json.Marshal(Response{Code:0,Data:map[string]any{},Msg:"成功",})res.Write(byteData)}funcmain(){http.HandleFunc("/get",GET)http.HandleFunc("/post",POST)fmt.Println("httpserverrunning:http://127.0.0.1:8080")http.ListenAndServe(":8080",nil)}但是在实际项目中使用原生gohttp库那会非常不方便。主要体现在参数解析与验证路由不太明了响应处理比较原始]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c190.html</guid><pubDate>Sat, 30 May 2026 13:40:18 +0800</pubDate></item><item><title><![CDATA[Go基础-20.go部署]]></title><link>https://www.beilika.com/article/c189.html</link><description><![CDATA[go项目的部署特别简单，编写完成之后，只需要执行gobuild即可打包为可执行文件注意，这个操作是不同平台不一样的windows下打包就是exe文件，linux下打包就是二进制文件打包命令gobuild打当前目录下的main包，注意，只能有一个main函数的包gobuildxxx.go打当前目录下，xxx.go的包，这个包必须得是一个main包，不然没有效果gobuild-omain.exexxx.go强制对输出的文件进行重命名-o参数必须得在文件的前面交叉编译什么是交叉编译呢，就是在windows上，我开发的go程序，我也能打包为linux上的可执行程序例如在windows平台，打linux的包注意，执行set这个命令，一定得要是在cmd的命令行下，powershell是无效的setCGO_ENABLED=0setGOOS=linuxsetGOARCH=amd64gobuild-omainmain.goCGO_ENABLED:CGO表示golang中的工具，CGO_ENABLED=0表示CGO禁用，交叉编译中不能使用CGOGOOS:环境变量用于指定目标操作系统，mac对应darwin，linux对应linux，windows对应windows，还有其它的freebsd、android等GOARCH：环境变量用于指定处理器的类型，386也称x86对应32位操作系统、amd64也称x64对应64位操作系统，arm这种架构一般用于嵌入式开发。比如Android，iOS，Winmobile等为了方便呢，可以在项目的根目录下写一个bat文件这样就能快速构建了然后放到linux服务器下，设置文件权限就可以直接运行了chmod+xmain./main需要注意，打包web项目的时候，配置文件和静态文件等这些非go程序，是要一起复制到目标服务器里面的]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c189.html</guid><pubDate>Sat, 30 May 2026 11:03:22 +0800</pubDate></item><item><title><![CDATA[Go基础-19.go网络编程]]></title><link>https://www.beilika.com/article/c188.html</link><description><![CDATA[TCP传输控制协议（TCP，TransmissionControlProtocol）是一种面向连接的、可靠的、基于字节流的传输层通信协议如何保证连接可靠呢？（面试常考题）三次握手四次挥手服务端packagemainimport("fmt""io""net")funcmain(){//创建tcp的监听地址tcpAddr,_:=net.ResolveTCPAddr("tcp","127.0.0.1:8080")//tcp监听listen,_:=net.ListenTCP("tcp",tcpAddr)for{//等待连接conn,err:=listen.AcceptTCP()iferr!=nil{fmt.Println(err)break}//获取客户端的地址fmt.Println(conn.RemoteAddr().String()+"进来了")//读取客户端传来的数据for{varbuf[]byte=make([]byte,1024)n,err:=conn.Read(buf)//客户端退出iferr==io.EOF{fmt.Println(conn.RemoteAddr().String()+"出去了")break}fmt.Println(string(buf[0:n]))}}}客户端packagemainimport("fmt""net")funcmain(){conn,_:=net.DialTCP("tcp",nil,&net.TCPAddr{IP:net.ParseIP("127.0.0.1"),Port:8080})varsstringfor{fmt.Scanln(&s)ifs=="q"{break}conn.Write([]byte(s))}conn.Close()}HTTP服务端packagemainimport"net/http"//handlerfunchandler(reshttp.ResponseWriter,req*http.Request){res.Write([]byte("hello枫枫"))}funcmain(){//回调函数http.HandleFunc("/index",handler)//绑定服务http.ListenAndServe(":8080",nil)}packagemainimport("encoding/json""fmt""io""net/http""os")funcIndexHandler(reshttp.ResponseWriter,req*http.Request){switchreq.Method{case"GET":data,err:=os.ReadFile("fengfengStudy/37.http/server/index.html")iferr!=nil{fmt.Println(data)}res.Write(data)//res.Write([]byte("hello枫枫GET"))case"POST"://获取body数据data,err:=io.ReadAll(req.Body)//拿请求头contentType:=req.Header.Get("Content-Type")fmt.Println(contentType)//switchcontentType{//case"application/json":////}iferr!=nil{fmt.Println(data)}ma:=make(map[string]string)json.Unmarshal(data,&ma)fmt.Println(ma["username"])typeUserstruct{Usernamestring`json:"username"`}varuserUserjson.Unmarshal(data,&user)fmt.Println(user)//设置响应头header:=res.Header()header["token"]=[]string{"y1gyf156sdgT%d44hjgj"}res.Write([]byte("hello枫枫POST"))}}funcmain(){//1.绑定回调http.HandleFunc("/index",IndexHandler)//2.注册服务fmt.Println("listenserveronhttp://localhost:8080")http.ListenAndServe(":8080",nil)}客户端packagemainimport("fmt""io""net/http")funcmain(){//实例化一个http客户端client:=new(http.Client)//构造请求对象req,_:=http.NewRequest("GET","http://localhost:8080/index",nil)//发请求res,_:=client.Do(req)//获取响应b,_:=io.ReadAll(res.Body)fmt.Println(string(b))}RPC远程过程调用的协议服务端packagemainimport("fmt""net""net/http""net/rpc")typeServerstruct{}typeReqstruct{Num1intNum2int}typeResstruct{Numint}func(sServer)Add(reqReq,res*Res)error{res.Num=req.Num1+req.Num2returnnil}funcmain(){//注册rpc服务rpc.Register(new(Server))rpc.HandleHTTP()listen,err:=net.Listen("tcp",":8080")iferr!=nil{fmt.Println(err)return}http.Serve(listen,nil)}客户端packagemainimport("fmt""net/rpc")typeReqstruct{Num1intNum2int}typeResstruct{Numint}funcmain(){req:=Req{1,2}client,err:=rpc.DialHTTP("tcp",":8080")iferr!=nil{fmt.Println(err)return}varresResclient.Call("Server.Add",req,&res)fmt.Println(res)}websocketwebsocket是socket连接和http协议的结合体，可以实现网页和服务端的长连接gogetgithub.com/gorilla/websocket服务端packagemainimport("fmt""github.com/gorilla/websocket""net/http")varUP=websocket.Upgrader{ReadBufferSize:1024,WriteBufferSize:1024,}funchandler(reshttp.ResponseWriter,req*http.Request){//服务升级conn,err:=UP.Upgrade(res,req,nil)iferr!=nil{fmt.Println(err)return}for{//消息类型，消息，错误t,p,err:=conn.ReadMessage()iferr!=nil{break}conn.WriteMessage(websocket.TextMessage,[]byte(fmt.Sprintf("你说的是：%s吗？",string(p))))fmt.Println(t,string(p))}deferconn.Close()fmt.Println("服务关闭")}funcmain(){http.HandleFunc("/",handler)http.ListenAndServe(":8080",nil)}服务端给多个客户端发packagemainimport("fmt""github.com/gorilla/websocket""net/http")varUP=websocket.Upgrader{ReadBufferSize:1024,WriteBufferSize:1024,}varconnLis[]*websocket.Connfunchandler(reshttp.ResponseWriter,req*http.Request){//服务升级conn,err:=UP.Upgrade(res,req,nil)iferr!=nil{fmt.Println(err)return}connLis=append(connLis,conn)for{//消息类型，消息，错误t,p,err:=conn.ReadMessage()iferr!=nil{break}forindex:=rangeconnLis{connLis[index].WriteMessage(websocket.TextMessage,[]byte(fmt.Sprintf("你说的是：%s吗？",string(p))))}fmt.Println(t,string(p))}deferconn.Close()fmt.Println("服务关闭")}funcmain(){http.HandleFunc("/",handler)http.ListenAndServe(":8080",nil)}客户端packagemainimport("bufio""fmt""github.com/gorilla/websocket""os")funcmain(){dl:=websocket.Dialer{}conn,_,err:=dl.Dial("ws://127.0.0.1:8080",nil)iferr!=nil{fmt.Println(err)return}gosend(conn)for{t,p,err:=conn.ReadMessage()iferr!=nil{break}fmt.Println(t,string(p))}}funcsend(conn*websocket.Conn){for{reader:=bufio.NewReader(os.Stdin)l,_,_:=reader.ReadLine()conn.WriteMessage(websocket.TextMessage,l)}}]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c188.html</guid><pubDate>Sat, 30 May 2026 11:00:14 +0800</pubDate></item><item><title><![CDATA[Go基础-18.go反射]]></title><link>https://www.beilika.com/article/c187.html</link><description><![CDATA[类型判断判断一个变量是否是结构体，切片，mappackagemainimport("fmt""reflect")funcrefType(objany){typeObj:=reflect.TypeOf(obj)fmt.Println(typeObj,typeObj.Kind())//去判断具体的类型switchtypeObj.Kind(){casereflect.Slice:fmt.Println("切片")casereflect.Map:fmt.Println("map")casereflect.Struct:fmt.Println("结构体")casereflect.String:fmt.Println("字符串")}}funcmain(){refType(struct{Namestring}{Name:"枫枫"})name:="枫枫"refType(name)refType([]string{"枫枫"})}通过反射获取值funcrefValue(objany){value:=reflect.ValueOf(obj)fmt.Println(value,value.Type())switchvalue.Kind(){casereflect.Int:fmt.Println(value.Int())casereflect.Struct:fmt.Println(value.Interface())casereflect.String:fmt.Println(value.String())}}通过反射修改值注意，如果需要通过反射修改值，必须要传指针，在反射中使用Elem取指针对应的值funcrefSetValue(objany){value:=reflect.ValueOf(obj)elem:=value.Elem()//专门取指针反射的值switchelem.Kind(){casereflect.String:elem.SetString("枫枫知道")}}funcmain(){name:="枫枫"refSetValue(&name)fmt.Println(name)}结构体反射读取json标签对应的值，如果没有就用属性的名称packagemainimport("fmt""reflect")typeStudentstruct{NamestringAgeint`json:"age"`}funcmain(){s:=Student{Name:"枫枫知道",Age:24,}t:=reflect.TypeOf(s)v:=reflect.ValueOf(s)fori:=0;i]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c187.html</guid><pubDate>Sat, 30 May 2026 10:45:49 +0800</pubDate></item><item><title><![CDATA[Go基础-17.go单元测试]]></title><link>https://www.beilika.com/article/c186.html</link><description><![CDATA[Go语言中自带有一个轻量级的测试框架testing和自带的gotest命令来实现单元测试和性能测试，testing框架和其他语言的测试框架相似，可以基于这个框架写针对相应函数的测试用例，也可以基于该框架写相应的压力测试用例。通过单元测试，可以解决：确保每个函数是可运行，并且运行结果是正确的确保写出来的代码性能是好的单元测试能及时的发现程序设计或实现的逻辑错误，使问题暴露，便于问题的定位解决，而性能测试的重点在于发现程序设计上的一些问题，让程序能够在高并发的情况下还能保持稳定Go语言推荐测试文件和源代码文件放在一块，测试文件以_test.go结尾需要注意的是测试用例文件名必须以_test.go结尾测试用例函数必须以Test开头，一般来说就是Test+被测试的函数名单元测试例如我现在有一个用于计算的文件，叫calc.gopackagemainfuncAdd(aint,bint)int{returna+b}funcMul(aint,bint)int{returna*b}那么我的测试文件就是calc_test.gopackagemainimport"testing"funcTestAdd(t*testing.T){ifans:=Add(1,2);ans!=3{//如果不符合预期，那就是测试不通过t.Errorf("1+2expectedbe3,but%dgot",ans)}ifans:=Add(-10,-20);ans!=-30{t.Errorf("-10+-20expectedbe-30,but%dgot",ans)}}测试的话，就在界面上点绿色的按钮就可以了如果是命令的话gotest//可以运行某个包下的所有测试用例-v参数会显示每个用例的测试结果-run参数可以指定测试某个函数单元测试框架提供的日志方法方法备注测试结果Log打印日志，同时结束测试PASSLogf格式化打印日志，同时结束测试PASSError打印错误日志，同时结束测试FAILErrorf格式化打印错误日志，同时结束测试FAILFatal打印致命日志，同时结束测试FAILFatalf格式化打印致命日志，同时结束测试FAIL子测试如果需要给一个函数，调用不同的测试用例，可以使用子测试子测试里面的Fatal，是不会终止程序的packagemainimport("testing")funcTestAdd(t1*testing.T){t1.Run("add1",func(t*testing.T){ifans:=Add(1,2);ans!=3{//如果不符合预期，那就是测试不通过t.Fatalf("1+2expectedbe3,but%dgot",ans)}})t1.Run("add2",func(t*testing.T){ifans:=Add(-10,-20);ans!=-30{t.Fatalf("-10+-20expectedbe-30,but%dgot",ans)}})}如果测试用例很多，还可以用一个类似表格去表示packagemainimport("testing")funcTestAdd(t*testing.T){cases:=[]struct{NamestringA,B,Expectedint}{{"a1",2,3,5},{"a2",2,-3,-1},{"a3",2,0,2},}for_,c:=rangecases{t.Run(c.Name,func(t*testing.T){ifans:=Add(c.A,c.B);ans!=c.Expected{t.Fatalf("%d*%dexpected%d,but%dgot",c.A,c.B,c.Expected,ans)}})}}TestMain函数它是测试的入口我们可以在TestMain里面实现测试流程的生命周期packagemainimport("fmt""os""testing")//测试前执行funcsetup(){fmt.Println("Beforealltests")}//测试后执行functeardown(){fmt.Println("Afteralltests")}funcTest1(t*testing.T){fmt.Println("I'mtest1")}funcTest2(t*testing.T){fmt.Println("I'mtest2")}//必须叫这个名字测试主入口funcTestMain(m*testing.M){//测试前执行setup()code:=m.Run()//测试后执行teardown()os.Exit(code)}]]></description><category>GoLang</category><guid>https://www.beilika.com/article/c186.html</guid><pubDate>Sat, 30 May 2026 10:34:58 +0800</pubDate></item></channel></rss>
