本来计划是学习Docker部署的,研究了一天没搞出来,得出结论是需要翻墙,懒得弄了,暂时放置。

一、以下是,上传/下载代码,和之前是重复的,只多添加了,上传/下载功能。
测试目录为工程根目录下/files文件夹内

package mainimport ("context""encoding/json""fmt""io""log""net/http""os""path/filepath""regexp""time""github.com/gin-gonic/gin""github.com/jackc/pgx/v5""github.com/jackc/pgx/v5/pgxpool"swaggerFiles "github.com/swaggo/files"ginSwagger "github.com/swaggo/gin-swagger"// 注意:替换为你项目的实际路径// _ "your_project/docs" // docs 包,由 swag 生成// 如果 docs 包在根目录,且 main.go 也在根目录,可以这样导入_ "HTTPServices/docs" // 假设 docs 目录在项目根目录下
)var db *pgxpool.Pool// 启动函数
func main() {// 初始化数据库连接db = InitDB()defer db.Close()// 注册路由RegisterRouter()// 启动 HTTP 服务go func() {StartHTTPServer()}()// 启动 HTTP api测试服务go func() {StartDebugHTTPServer()}()// 阻塞主线程select {}
}// @title           Sample API
// @version         1.0
// @description     API测试页面
// @host      localhost:8080
func StartDebugHTTPServer() {r := gin.Default()// --- 挂载 Swagger UI ---// 访问 http://localhost:8081/swagger/index.html 查看 UIr.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))port := ":8081"LogSuccess("启动 HTTP Swagger测试服务启动,监听端口 %s\n", port)// 启动服务器debugApiError := r.Run(port)if debugApiError != nil {LogError("HTTP api测试服务启动失败:%v", debugApiError)} else {LogSuccess("HTTP api测试服务已启动,监听端口 8081")}
}// 启动 HTTP 服务
func StartHTTPServer() {address := "127.0.0.1:8080" //配置连接ip端口//配置跨域,是影响调试页面不能访问8080相关地址的原因handler := corsMiddleware(http.DefaultServeMux)LogSuccess("启动 HTTP 服务,监听端口 %s\n", address)err := http.ListenAndServe(address, handler)if err != nil {log.Fatalf("服务器启动失败:%v", err)}
}// corsMiddleware 是一个中间件,用于添加 CORS 头
func corsMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// 设置 CORS 响应头w.Header().Add("Access-Control-Allow-Origin", "http://localhost:8081") // ✅ 修改为你的前端地址w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")w.Header().Set("Access-Control-Allow-Headers","Origin, Content-Type, Accept, Authorization, X-Requested-With")// 如果需要传递 Cookie 或 Authorization Bearer Tokenw.Header().Set("Access-Control-Allow-Credentials", "true")// 处理预检请求 (OPTIONS)if r.Method == "OPTIONS" {w.WriteHeader(http.StatusOK)return}// 调用下一个处理器 (即注册的路由)next.ServeHTTP(w, r)})
}// 注册路由
func RegisterRouter() {http.HandleFunc("/", helloHandler)    //http://localhost:8080/http.HandleFunc("/time", timeHandler) //http://localhost:8080/time//查询http.HandleFunc("/findTable", findTableNameHandler) //http://localhost:8080/findTable?tableName=name//添加http.HandleFunc("/addTable1", addTable1Handler) //http://localhost:8080/addTable1//删除http.HandleFunc("/deleteTableValue", deleteTableHandler) //http://localhost:8080/deleteTableValue?tableName=table1&fieldName=test1&fieldValue=123test//修改http.HandleFunc("/updateTableValue", updateTableHandler) //http://localhost:8080/updateTableValue?tableName=table1&findFieldName=test1&findFieldValue=hello&setFieldName=test3&setFieldValue=456//下载文件http.HandleFunc("/downloadFile", downloadHandler) //http://localhost:8080/downloadFile?filePath=D:\GoProject\HTTPServices\README.md//上传文件http.HandleFunc("/uploadFile", uploadHandler) //http://localhost:8080/uploadFile}// APIResponse 定义了统一的 API 响应格式
type APIResponse struct {Success   bool        `json:"success"`           // 是否成功Status    int         `json:"status"`            // HTTP 状态码Message   string      `json:"message,omitempty"` // 简短消息 报错时的提示信息Data      interface{} `json:"data,omitempty"`    // 主要数据内容Timestamp string      `json:"timestamp"`         // 时间戳 (秒)
}// SendJSONResponse 封装了 JSON 响应的发送逻辑
func SendJSONResponse(w http.ResponseWriter, success bool, status int, message string, data interface{}) {// 设置 Content-Typew.Header().Set("Content-Type", "application/json")// 设置 HTTP 状态码w.WriteHeader(status)// 构造响应体response := APIResponse{Success:   success,Status:    status,Message:   message,Data:      data,Timestamp: time.Now().Format("2006-01-02 15:04:05"), // 当前时间戳格式化}// 编码并发送 JSONif err := json.NewEncoder(w).Encode(response); err != nil {// 如果编码失败,记录错误(但不能再次写入 w,因为 Header 已经发送)http.Error(w, "Internal Server Error", http.StatusInternalServerError)// log.Printf("JSON encode error: %v", err) // 取消注释以记录日志}
}// @Summary      根目录测试连接
// @Description
// @Tags         tags1
// @Accept       json
// @Produce      json
// @Router       / [get]
func helloHandler(w http.ResponseWriter, r *http.Request) {LogInfo("访问路径:%s,来源:%s\n", r.URL.Path, r.RemoteAddr)// 编码 JSON 响应SendJSONResponse(w, true, http.StatusOK, "成功", "Hello, World! 👋")
}// @Summary      查询服务器时间
// @Description
// @Tags         tags1
// @Accept       json
// @Produce      json
// @Router       /time [get]
func timeHandler(w http.ResponseWriter, r *http.Request) {LogInfo("访问路径:%s,来源:%s\n", r.URL.Path, r.RemoteAddr)currentTime := time.Now().Format("2006-01-02 15:04:05")// ✅ 设置响应头SendJSONResponse(w, true, http.StatusOK, "成功", currentTime)
}// @Summary      修改指定表名中,find字段名等于指定值的set字段名的数据
// @Description  根据提供的表名、find字段名、find字段值、set字段名、set字段值,修改数据库中的数据。
// @Tags         tags1
// @Produce      json
// @Param        tableName query string true  "要查询的数据库表名" default(table1)
// @Param        fieldName query string true  "要查询的字段名"
// @Param        fieldValue query string true  "要查询的字段值"
// @Param        setFieldName query string true  "要更新的字段名"
// @Param        setFieldValue query string true  "要更新的字段值"
// @Router       /updateTableValue [get]
func updateTableHandler(w http.ResponseWriter, r *http.Request) {// 解析请求参数tableName := r.URL.Query().Get("tableName")findFieldName := r.URL.Query().Get("findFieldName")findFieldValue := r.URL.Query().Get("findFieldValue")setFieldName := r.URL.Query().Get("setFieldName")setFieldValue := r.URL.Query().Get("setFieldValue")// 完整的参数验证if tableName == "" || findFieldName == "" || setFieldName == "" {http.Error(w, "缺少必要参数", http.StatusBadRequest)return}// 🔐 白名单验证 - 只允许预定义的表和字段allowedTables := map[string]bool{"table1": true, "table2": true}allowedFields := map[string]bool{"test1": true, "test2": true, "test3": true,"test4": true, "test5": true, "test6": true, "test7": true,}if !allowedTables[tableName] {http.Error(w, "不允许的表名", http.StatusBadRequest)return}if !allowedFields[findFieldName] || !allowedFields[setFieldName] {http.Error(w, "不允许的字段名", http.StatusBadRequest)return}// ✅ 使用参数化查询,表名和字段名通过白名单验证后拼接query := fmt.Sprintf("UPDATE %s SET %s = $1 WHERE %s = $2",tableName, setFieldName, findFieldName,)result, err := db.Exec(context.Background(), query, setFieldValue, findFieldValue)if err != nil {http.Error(w, "更新数据失败: "+err.Error(), http.StatusInternalServerError)return}// 检查是否实际更新了数据rowsAffected := result.RowsAffected()if rowsAffected == 0 {http.Error(w, "未找到匹配的数据进行更新", http.StatusNotFound)return}SendJSONResponse(w, true, http.StatusOK, "成功", fmt.Sprintf("%d 行已更新", rowsAffected))}// @Summary      删除指定表名中,指定字段名等于指定值的数据
// @Description  根据提供的表名和字段名和值,删除数据库中的数据。
// @Tags         tags1
// @Produce      json
// @Param        tableName query string true  "要删除的数据库表名"
// @Param        fieldName query string true  "要删除的字段名"
// @Param        fieldValue query string true  "要删除的字段值"
// @Router       /deleteTableValue [get]
func deleteTableHandler(w http.ResponseWriter, r *http.Request) {// 解析请求参数tableName := r.URL.Query().Get("tableName")fieldName := r.URL.Query().Get("fieldName")fieldValue := r.URL.Query().Get("fieldValue")if tableName == "" || fieldName == "" || fieldValue == "" {http.Error(w, "参数错误", http.StatusBadRequest)return}// 执行 SQL 语句,使用参数化查询query := fmt.Sprintf("DELETE FROM %s WHERE %s = $1", tableName, fieldName)_, err := db.Exec(context.Background(), query, fieldValue)if err != nil {http.Error(w, "删除数据失败: "+err.Error(), http.StatusInternalServerError)return}SendJSONResponse(w, true, http.StatusOK, "成功", "数据已删除")
}// @Summary      向table1表中添加数据,字段名=test1,test2,test3,test4,test5,test6,test7
// @Description  根据提供的json数据,向数据库table1中添加数据。
// @Tags         tags1
// @Produce      json
// @Param        data body string true "要插入的数据对象"
// @Router       /addTable1 [post]
func addTable1Handler(w http.ResponseWriter, r *http.Request) {// 定义需要插入的数据结构type requestData struct {Test1 string    `json:"test1"`Test2 time.Time `json:"test2"`Test3 uint32    `json:"test3"`Test4 string    `json:"test4"`Test5 float64   `json:"test5"`Test6 int32     `json:"test6"`Test7 float64   `json:"test7"`}// 解析请求参数var data requestDataerr := json.NewDecoder(r.Body).Decode(&data)if err != nil {http.Error(w, "解析请求参数失败: "+err.Error(), http.StatusBadRequest)return}// 执行 SQL 语句,使用参数化查询query := "INSERT INTO table1 (test1, test2, test3, test4, test5, test6, test7) VALUES ($1, $2, $3, $4, $5, $6, $7)"_, err = db.Exec(context.Background(), query, data.Test1, data.Test2, data.Test3, data.Test4, data.Test5, data.Test6, data.Test7)if err != nil {http.Error(w, "插入数据失败: "+err.Error(), http.StatusInternalServerError)return}SendJSONResponse(w, true, http.StatusOK, "成功", "数据已插入")
}// @Summary      查询指定表名的全部数据
// @Description  根据提供的表名查询数据库中的所有数据。
// @Tags         tags1
// @Produce      json
// @Param        tableName query string true  "要查询的数据库表名" default(table1)
// @Router       /findTable [get]
func findTableNameHandler(w http.ResponseWriter, r *http.Request) {tableName := r.URL.Query().Get("tableName")if tableName == "" {http.Error(w, "tableName is empty", http.StatusBadRequest)return}// ✅ 安全校验表名(防止 SQL 注入)if !isValidTableName(tableName) {http.Error(w, "invalid table name", http.StatusBadRequest)return}// ✅ 使用参数化方式拼接表名(仅限对象名,如表、字段)query := fmt.Sprintf("SELECT * FROM %s", tableName)rows, err := db.Query(context.Background(), query)if err != nil {http.Error(w, "查询失败: "+err.Error(), http.StatusInternalServerError)return}defer rows.Close()// ✅ 使用 pgx 内置工具自动转为 []map[string]interface{}data, err := pgx.CollectRows(rows, pgx.RowToMap)if err != nil {http.Error(w, "解析数据失败: "+err.Error(), http.StatusInternalServerError)return}SendJSONResponse(w, true, http.StatusOK, "成功", data)
}// 安全校验表名(防止 SQL 注入)
func isValidTableName(name string) bool {// 只允许字母、数字、下划线,且不能以数字开头matched, _ := regexp.MatchString(`^[a-zA-Z_][a-zA-Z0-9_]*$`, name)return matched
}// @Summary      下载文件
// @Description
// @Tags         tags1
// @Produce      json
// @Router       /downloadFile [get]
func downloadHandler(w http.ResponseWriter, r *http.Request) {fileName := "testdownloadFIle.txt"// 要下载的文件路径filePath := "./files/" + fileName // 假设文件在项目根目录下的 files 文件夹中// 设置响应头,提示浏览器下载(可选)w.Header().Set("Content-Disposition", "attachment; filename="+fileName)// 如果不设置,浏览器可能会尝试直接打开文件(如PDF、图片)// 使用 http.ServeFile 提供文件http.ServeFile(w, r, filePath)
}// uploadHandler 处理文件上传
// @Summary      上传文件
// @Description  支持上传任意文件,最大 50MB
// @Tags         tags1
// @Accept       multipart/form-data
// @Produce      json
// @Param        file formData file true "要上传的文件"
// @Success      200 {string} string "文件上传成功"
// @Failure      400 {object} map[string]string "请求错误,如文件太大或格式错误"
// @Failure      500 {object} map[string]string "服务器内部错误"
// @Router       /uploadFile [post]
func uploadHandler(w http.ResponseWriter, r *http.Request) {if r.Method != "POST" {http.Error(w, "只支持POST方法", http.StatusMethodNotAllowed)return}// 解析 multipart form  文件大小限制if err := r.ParseMultipartForm(50 << 20); err != nil { // 50MB限制http.Error(w, "文件太大", http.StatusBadRequest)return}// 获取文件file, handler, err := r.FormFile("file")if err != nil {http.Error(w, "获取文件失败: "+err.Error(), http.StatusBadRequest)return}defer file.Close()// 创建目标文件filename := fmt.Sprintf("%d_%s", time.Now().UnixNano(), handler.Filename)dstPath := filepath.Join("./files/", filename)dst, err := os.Create(dstPath)if err != nil {http.Error(w, "创建文件失败: "+err.Error(), http.StatusInternalServerError)return}defer dst.Close()// 复制文件内容if _, err := io.Copy(dst, file); err != nil {http.Error(w, "保存文件失败: "+err.Error(), http.StatusInternalServerError)return}// 返回响应SendJSONResponse(w, true, http.StatusOK, "成功", "文件上传成功")
}

二、便捷初始化swagger+直接启动服务小工具。
测试的时候发现总是忘记执行swagger的初始化命令,导致swagger界面不出现新添加的接口,还要反应半天才明白过来是没有执行 swag init。
直接搞了个启动脚本,在工程根目录创建runTools.bat文件

@echo off
:: 设置代码页为 UTF-8,正确显示中文
chcp 65001 >nul
swag init
if %ERRORLEVEL% EQU 0 (echo ===swagger 初始化完成!===
go run .
) else (echo ===swagger 初始化失败!===
)

这样之后每次运行服务,只要在终端输入.\runTools.bat即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/96516.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96516.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/96516.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL中对视图的操作命令汇总

以下是基于搜索结果整理的SQL视图操作命令汇总&#xff0c;按功能分类说明&#xff1a; 一、创建视图 使用 CREATE VIEW 语句定义视图&#xff0c;需指定视图名称和基础查询表达式&#xff1a; CREATE VIEW view_name AS SELECT column1, column2, ... FROM table_name WHER…

【Spring Cloud 微服务】2.守护神网关Gateway

目录 1.API网关的作用 2.Spring Cloud Gateway 是什么&#xff1f; 3.核心由来与背景 1. 微服务架构的挑战&#xff1a; 2. API 网关模式的兴起&#xff1a; 3. Zuul 的局限性&#xff1a; 4. Spring Cloud Gateway 的诞生&#xff1a; 4.核心特征&#xff1a; 5.核心概…

解读商业智能BI,数据仓库中的元数据

之前的文章讨论过数据分析、数据治理、数据仓库等等&#xff0c;即使是非业内人员从字面意思&#xff0c;也是可以了解一二的&#xff0c;但是&#xff0c;很多人对于元数据可能就比较陌生了。那么&#xff0c;今天我们就来聊一聊元数据管理。数据仓库要说元数据&#xff0c;那…

3 种无误的方式删除 Itel 手机上的短信

如果你希望释放存储空间、保护隐私&#xff0c;或者准备出售或转让手机&#xff0c;删除 Itel 手机上的短信是一个实用的步骤。无论是收件箱中充斥着垃圾短信、过时的对话还是敏感内容&#xff0c;删除不需要的短信可以让你的消息体验更加干净和安全。本文将向你介绍 3 种简单且…

【学习笔记】网络安全专用产品类别与参考标准

一、基本标准 1.1 关键设备 网络关键设备认证依据的强制标准为 GB 40050-2021。 1.2 专用产品 网络安全专用产品认证依据的强制标准为 GB 42250-2022。 二、数据备份与恢复产品标准 相关标准&#xff1a; GB/T 29765-2021《信息安全技术 数据备份与恢复产品技术要求与测试评…

Pytho“张量”(Tensor)和 Java的“向量”(Vector)区别和联系

在Python和Java中&#xff0c;“张量”&#xff08;Tensor&#xff09;和“向量”&#xff08;Vector&#xff09;是两个不同语境下的概念&#xff0c;它们的设计目标、功能和应用场景存在显著差异&#xff0c;但也存在一定的共性。以下从区别和联系两方面详细说明&#xff1a;…

Ubuntu部署K8S集群

Ubuntu部署K8S集群 本例以三台Ubuntu24.04为例,1master节点2worker节点 环境准备 修改hostname,三台服务器分别执行 hostnamectl set-hostname k8s-master01hostnamectl set-hostname k8s-worker01hostnamectl set-hostname k8s-worker02 配置静态ip(不同系统修改方法略微差…

openEuler系统安装Ascend Docker Runtime的方法

在openEuler系统中使用NPU前一定要安装Ascend Docker Runtime,也是在安装CANN和mis-tei前的必备工作。 使用容器化支持、整卡调度、静态vNPU调度、动态vNPU调度、断点续训、弹性训练、推理卡故障恢复或推理卡故障重调度的用户,必须安装Ascend Docker Runtime。 下面是具体的安…

控制对文件的访问:Linux 文件系统权限管理总结

在 Linux 系统中&#xff0c;文件权限是保障系统安全和数据完整性的核心机制。红帽企业 Linux 9.0通过一套灵活且精细的权限控制体系&#xff0c;让用户能够精确管理文件和目录的访问范围。本章将系统梳理 Linux 文件系统权限的核心概念、管理方法及高级应用&#xff0c;为系统…

ansible中roles角色是什么意思?

文章目录一、介绍二、Ansible Roles目录编排三、创建role四、playbook调用角色五、roles中tags使用免费个人运维知识库&#xff0c;欢迎您的订阅&#xff1a;literator_ray.flowus.cn 一、介绍 角色是ansible自1.2版本引入的新特性&#xff0c;用于层次性、结构化地组织playbo…

pytorch 网络可视化

1.torchsummary在 Anaconda prompt 中进入自己的 pytorch 环境&#xff0c;安装依赖包。 bash pip install torchsummary 2.tensorboardX 3. graphviz torchviz 4.Jupyter Notebook tensorwatch 5.netron 6.hiddenlayer 7.PlotNeuralNet

可以一键生成PPT的AI PPT工具(最新整理)

在当今快节奏的职场环境中&#xff0c;高效制作专业PPT已成为一项必备技能。传统PPT制作流程耗时费力&#xff0c;从构思大纲、搜集资料、撰写内容到设计排版&#xff0c;往往需要数小时甚至数天时间。AI生成PPT工具的兴起彻底改变了这一局面&#xff0c;让职场人士能够专注于内…

数仓核心概念阐述

数仓核心概念阐述一、数据仓库建模模型二、数据处理架构三、流批处理架构演进**为什么需要流批融合&#xff1f;****1. Lambda 架构&#xff08;双引擎护航&#xff09;****2. Kappa 架构&#xff08;流处理一统江湖&#xff09;****关键概念对照表****实际案例理解****演进趋势…

Spring Boot 自动配置全流程深度解析

在 Spring Boot 的世界里&#xff0c;“约定优于配置” 理念通过自动配置机制展现得淋漓尽致。从一个简单的SpringBootApplication注解开始&#xff0c;背后隐藏着一套精妙的自动配置加载流程。本文将从SpringBootApplication出发&#xff0c;逐步拆解自动配置类是如何被发现、…

AI:业务驱动与技术赋能:企业智能化应用的双向进化深度指南

一、业务与技术的双螺旋进化模型 1.1 从单向适配到双向驱动的认知转变 传统的信息化建设往往遵循"业务提需求、技术做实现"的线性模式&#xff0c;这种模式在稳定的业务环境中确实有效&#xff0c;但在当前快速变化的数字化时代已经显露出明显的局限性。真正的数字化…

2721. 【SDOI2010】外星千足虫

2721. 【SDOI2010】外星千足虫 题解 题目描述 题目描述 公元2089年6月4日&#xff0c;在经历了17年零3个月的漫长旅行后&#xff0c;“格纳格鲁一号”载人火箭返回舱终于安全着陆。此枚火箭由美国国家航空航天局&#xff08;NASA&#xff09;研制发射&#xff0c;行经火星、…

[RestGPT] RestGPT智能体

第3章&#xff1a;RestGPT智能体 欢迎回来&#x1f43b;‍❄️ 在第1章&#xff1a;配置与环境中&#xff0c;我们为RestGPT配备了必要的"钥匙和密码"&#xff1b;在第2章&#xff1a;OpenAPI规范(OAS)中&#xff0c;我们为它提供了与在线服务对话的"使用说明…

笔记本电脑Windows+Ubuntu 双系统,Ubuntu无法挂载Windows的硬盘 报错问题解决

目录 一、前情提要 二、解决方案步骤 第一步&#xff1a;进入Windows进行修复和检查。这是最关键的一步&#xff0c;目的是让Windows来检查和修复它自己的文件系统。 第二步&#xff1a;回到Ubuntu验证挂载 三、总结与预防 一、前情提要 网上找到许多解决方案&#xff0c…

加密货币与区块链:六大刑事重灾区

高鹏律师&#xff08;首席数据官&#xff09;数字经济团队创作&#xff0c;AI辅助在数字货币的世界里&#xff0c;一夜暴富的传说屡见不鲜&#xff0c;但顷刻间失去所有的悲剧也时有发生&#xff0c;现在&#xff0c;我将为您剖析加密货币与区块链领域的六大刑事风险重灾区&…

Spring Ai 1.0.1中存在的问题:使用MessageChatMemoryAdvisor导致System未被正确的放在首位

使用MessageChatMemoryAdvisor导致System未被正确的放在首位 如下是使用Spring Ai实现多轮对话的官方例子&#xff08;文档地址&#xff1a;https://docs.spring.io/spring-ai/reference/api/chat-memory.html&#xff09;&#xff1a;AutowiredChatMemoryRepository chatMemor…