报错信息

http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192

高版本http废除了分号做分隔符,会在http库中做报警输出,基础库代码如下:

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
} if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {
var allowQuerySemicolonsInUse int32
req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() {
atomic.StoreInt32(&allowQuerySemicolonsInUse, 1)
}))
defer func() {
if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 {
sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")
}
}()
} handler.ServeHTTP(rw, req)
}

解决方法有两种

1、http中不要用分号做分隔符,不要含有分号

2、调用库函数http.AllowQuerySemicolons()允许分号,解除报错

由于项目历史版本缘故,无法完全做到第1点,需要做第2点来补充。如果直接调用http.AllowQuerySemicolons,发现其就是简单粗暴将分号直接替换成&(代码如下),从而可能会引发后续http body解析报错

func AllowQuerySemicolons(h Handler) Handler {
return HandlerFunc(func(w ResponseWriter, r *Request) {
if silenceSemicolonsWarning, ok := r.Context().Value(silenceSemWarnContextKey).(func()); ok {
silenceSemicolonsWarning()
}
if strings.Contains(r.URL.RawQuery, ";") {
r2 := new(Request)
*r2 = *r
r2.URL = new(url.URL)
*r2.URL = *r.URL
r2.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&")
h.ServeHTTP(w, r2)
} else {
h.ServeHTTP(w, r)
}
})
}

最终解决方案

1、将http内容中的分号替换,并记录位置

2、调用http.AllowQuerySemicolons,解除报警输出

3、将1中被替换的分号还原

4、调用handler处理函数,解析参数,进行业务逻辑

最终代码如下:

package main

import (
"context"
"fmt"
"net/http"
"regexp"
"strings"
"time"
"unsafe"
) // BytesToString converts byte slice to string.
func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
} func MarkQuerySemicolons(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.RawQuery, ";") {
// 正则匹配 将原url中的';'索引位置标记放到context中
semicolonsIndexes := regexp.MustCompile(";").FindAllStringIndex(r.URL.RawQuery, -1)
// 索引信息放到context中 在具体处理方法前再根据context中存的下标还原';'的位置
r = r.WithContext(context.WithValue(r.Context(), "semicolonMarks", semicolonsIndexes))
r.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&")
h.ServeHTTP(w, r)
} else {
h.ServeHTTP(w, r)
}
}
} func RecoverRawQuerySemicolons(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
marksContextV := r.Context().Value("semicolonMarks")
semicolonsIndexes, ok := marksContextV.([][]int)
if ok {
urlReduceSemicolons := []byte(r.URL.RawQuery)
for _, index := range semicolonsIndexes {
if len(index) != 0 {
urlReduceSemicolons[index[0]] = ';'
}
}
r.URL.RawQuery = BytesToString(urlReduceSemicolons)
}
next.ServeHTTP(w, r)
})
} func HandleRawQuerySemicolons(next http.Handler) http.HandlerFunc {
return MarkQuerySemicolons(http.AllowQuerySemicolons(RecoverRawQuerySemicolons(next)))
} func Health(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path, r.URL.RawQuery)
w.Write([]byte("ok"))
} func main() {
http.HandleFunc("/health", HandleRawQuerySemicolons(http.HandlerFunc(Health)))
http.HandleFunc("/health2", Health)
s := &http.Server{
Addr: ":10099",
Handler: nil,
ReadTimeout: time.Second,
WriteTimeout: time.Second,
MaxHeaderBytes: 1 << 20,
}
if err := s.ListenAndServe(); err != nil {
fmt.Println(err.Error())
}
}

测试情况

分别请求

http://127.0.0.1:10099/health?a=1;b=2

http://127.0.0.1:10099/health2?a=1;b=2

Health中输出如下:

/health a=1;b=2
/health2 a=1;b=2
2023/05/16 15:17:18 http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192

测试OK。直接调用Health,引发了报错信息。用HandleRawQuerySemicolons包装的没有报错,且url内容正常。

参考文档

https://www.cnblogs.com/tinfy/archive/2023/01/13/17049049.html

Golang URL query contains semicolon 报错解决方案的更多相关文章

  1. mysql主从复制报错解决方案

    mysql主从复制报错解决方案 我先制造个错误 在slave删除个info3字段 然后在master 在info3插入数据 报错如下<pre> Last_SQL_Errno: 1054 L ...

  2. RabbitMQ>Erlang machine stopped instantly (distribution name conflict?). The service is not restarted as OnFail is set to ignore.-报错解决方案 原来是NNND。。。

    >Erlang machine stopped instantly (distribution name conflict?). The service is not restarted as ...

  3. Updates were rejected because the remote contains work that you do(git报错解决方案)

    Updates were rejected because the remote contains work that you do(git报错解决方案) 今天向GitHub远程仓库提交本地项目文件时 ...

  4. JMeter 报告监听器导入.jtl结果文件报错解决方案

    JMeter 报告监听器导入.jtl结果文件报错解决方案   by:授客 QQ:1033553122   1. 问题描述 把jmeter压测时生成的 .jtl结果文件导入监听器报告中,弹出如下错误提示 ...

  5. IE浏览器url中带中文报错的问题;以及各种兼容以及浏览器问题总结

    1.解决IE浏览器url带中文报错 /* encodeURI()解决IE浏览器请求url中带中文报错的问题 */ URL = encodeURI("<%=basePath%>ve ...

  6. Python3.x:import urllib2报错解决方案

    Python:import urllib2报错解决方案 python2和3有些不一样: python2:输出为print 'hello world' python3:输出为print('hello w ...

  7. php 500报错解决方案

    php 500报错解决方案 1 先看nginx error.log 指定的错误日记文件路径 找到这个日记文件看 里面信息 2 再看 php-fpm.conf 里面指定的PHP错误日记的路径 具体如下& ...

  8. 转:CentOS上安装LAMP之第三步:MySQL环境及安装过程报错解决方案(纯净系统环境)

    这是AMP运行环境中最后配置的环境: 惯例传送门: 1.编译安装MySQL cd /home/zhangatle/tar tar zxvf mysql-.tar.gz cd mysql- cmake ...

  9. Eclipse开发Android项目报错解决方案详细教程,最新版一篇就够了!

    本文记录刚接触Android开发搭建环境后新建工程各种可能的报错,并亲身经历漫长的解决过程(╥╯^╰╥),寻找各种偏方,避免大家采坑,希望能帮助到大家. 报错信息 出错一:The import and ...

  10. 【笔记】springCloud--Alibaba--nacos介绍----启动报错解决方案

    Nacos介绍 · 欢迎来到 Nacos 的世界! · Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现.服务配置.服务元数据及流量管 ...

随机推荐

  1. 没有调度器的协程不是好协程——零基础深入浅出 C++20 协程

    前言 上一篇<协程本质是函数加状态机>谈到 C++20 协程的本质,是编译器基于 duff device 的精巧封装,经过一番乾坤大挪移,协程体内容被掉包只保留协程初始化代码,实际运行代码 ...

  2. 有哪些值得推荐的ESB厂商

    国内比较好的esb厂商有哪些 企业要想追求创新性转型,就必须要实现企业内部系统的互联互通以及与外部合作伙伴的高效协作,而企业服务总线(ESB)作为实现系统集成的关键技术,是企业发展路径上不可多得的助力 ...

  3. ubuntu上搭建ChatGLM2-6b环境及ptuing微调训练的坑

    清华大学的chatGLM2-6B可以说是目前亚洲唯一能打的对中文支持不错的LLM大模型,网上已经有很多介绍如何在本机搭建环境的文章,就不再重复了,这里记录下最近踩的一些坑: 1.为啥要使用ubuntu ...

  4. Rust: 如何用bevy写一个贪吃蛇(上)

    bevy社区有一篇不错的入门教程:Creating a Snake Clone in Rust, with Bevy,详细讲解了贪吃蛇的开发过程,我加了一些个人理解,记录于此: 一.先搭一个" ...

  5. EvalEx示例

    项目做久了,难免会发现有些需求总是反复无常,今天想这样,明天想那样,但是逻辑本身又不是很复杂,比如: A.很多系统为了鼓励用户,会出一些奖励政策:连续打卡(或登录)X天,奖虚拟币Y枚.但是这个X,Y的 ...

  6. SQL 解析在 CloudQuery 中的应用

    hi 好久不见!今天将为大家带来一期干货满满的技术分享. 作为一款数据库管控平台,大家通常认为 CloudQuery 的核心能力是对平台的管控,包括统一入口管理.权限体系.审计分析等,但实际上 Clo ...

  7. AI数字人制作工具SadTalker教程

    AI数字人(Artificial Intelligence Digital Human)是一种采用人工智能技术和仿真技术创建的虚拟人物.它结合了人类外貌.语音和认知能力,能够与人类进行交流和互动. A ...

  8. Prometheus 监控 Kubernetes Cluster 最新极简教程

    Kubernetes 是在生产中运行容器化工作负载的最流行的编排器.它为您提供了一套完整的工具,用于部署.扩展和管理容器. 不过,仅靠 Kubernetes 不足以运维应用程序.您还需要了解集群利用率 ...

  9. [笔记]树形dp - 2/4(树上背包类)

    树上背包是树形dp的常见模型,通常是分组背包的变形,而分组背包的本质就是多个泛化物品不断相加.因此掌握泛化物品的合并的方法有助于理解转移的过程(具体见1.4). 此类问题一般也可以用DFS序.多叉转二 ...

  10. 本地部署 DeepSeek-R1-671B 满血版大模型教程

    DeepSeek-R1大模型具备深度思考和推理能力,在数学.代码.自然语言推理等任务上都有着极大的提升.一方面由于官方或第三方的在线服务或多或少存在不稳定的问题,另一方面考虑到数据安全和隐私问题,本地 ...