goalng框架Gin解析

         本文通过案例的形式,说明gin框架的基本用法,主要列举后端的案例,前端和相对简单的知识点未在此分析;

          过完案例后可以有个基本的印象:就是封装和简便

package mainimport ("fmt""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""github.com/gin-gonic/gin/testdata/protoexample""github.com/go-playground/locales/en""github.com/go-playground/locales/zh""github.com/go-playground/locales/zh_Hant_TW"ut "github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"en_translations "github.com/go-playground/validator/v10/translations/en"zh_translations "github.com/go-playground/validator/v10/translations/zh"zh_tw_translations "github.com/go-playground/validator/v10/translations/zh_tw""github.com/gorilla/sessions""io""log""net/http""os""strings""time"
)func main() {JsonTransBind()
}func Hello() {//创建路由r := gin.Default()//绑定路由规则执行的函数//gin.Context 封装了request和responser.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "Hello World!!!!")})r.Run("localhost:8080")
}func GinParam() {r := gin.Default()r.GET("/user/:name/*action", func(c *gin.Context) {name := c.Param("name")action := c.Param("action")fmt.Println("action:" + action)//解析action = strings.Trim(action, "/")c.String(http.StatusOK, "%s is %s", name, action)})r.Run(":8080")
}func UrlParam() {r := gin.Default()r.GET("/user", func(c *gin.Context) {//指定默认值name := c.DefaultQuery("name", "gogo")c.String(http.StatusOK, "hello  %s", name)})r.Run(":8080")
}// 表单参数
func PostForm() {r := gin.Default()r.POST("/form", func(c *gin.Context) {types := c.DefaultPostForm("type", "post")username := c.PostForm("username")password := c.PostForm("password")c.String(http.StatusOK, "%s %s type is %s ", username, password, types)})r.Run(":8080")
}// 上传单个文件
func UploadFile() {r := gin.Default()//限制上传最大尺寸r.MaxMultipartMemory = 8 << 20r.POST("/upload", func(c *gin.Context) {file, err := c.FormFile("file")if err != nil {c.String(500, "上传图片出错")}c.SaveUploadedFile(file, file.Filename)c.String(http.StatusOK, file.Filename)})r.Run(":8080")
}// 上传特定文件
func UploadSpecifyFile() {r := gin.Default()r.POST("/upload", func(c *gin.Context) {_, header, err := c.Request.FormFile("file")if err != nil {log.Println("Error when try to get file: %v", err)}//headers.Size 获取文件大小if header.Size > 1024*1024*2 {fmt.Println("文件太大了")return}//if header.Header.Get("Content-Type") != "image/png" {fmt.Println("文件格式错误")c.String(http.StatusOK, "文件格式错误")return}c.SaveUploadedFile(header, header.Filename)c.String(http.StatusOK, header.Filename)})r.Run(":8080")
}// Json 数据解析和绑定
// 定义接收数据的结构体
type Login struct {// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}func JsonTransBind() {r := gin.Default()r.POST("/loginJson", func(c *gin.Context) {//声明接收的变量var json Login//将request body中的数据按照JSON格式解析成结构体err := c.ShouldBindBodyWithJSON(&json)if err != nil {//返回错误信息,gin.H封装了生成JSON数据的工具c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}//判断用户名密码是否正确if json.User != "root" || json.Pssword != "admin" {c.JSON(http.StatusBadRequest, gin.H{"status": "304"})return}c.JSON(http.StatusOK, gin.H{"status": "200"})})r.Run(":8080")
}// 表单数据解析和绑定
func FormTransBind() {r := gin.Default()r.POST("/loginForm", func(c *gin.Context) {var form Login// Bind()默认解析并绑定form格式// 根据请求头中content-type自动推断err := c.Bind(&form)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if form.User != "root" || form.Pssword != "admin" {c.JSON(http.StatusBadRequest, gin.H{"status": "304"})}c.JSON(http.StatusOK, gin.H{"status": "200"})})r.Run(":8080")
}//URI数据解析和绑定func URIBind() {r := gin.Default()r.GET("/:user/:password", func(c *gin.Context) {//声明接收的变量var login Loginerr := c.ShouldBindUri(&login)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if login.User != "root" || login.Pssword != "admin" {c.JSON(http.StatusBadRequest, gin.H{"status": "304"})return}c.JSON(http.StatusOK, gin.H{"status": "200"})})r.Run(":8080")
}//各种数据格式的响应  json、结构体、XML、YAML类似于java的properties、ProtoBuffunc SomeStructResponse() {r := gin.Default()//jsonr.GET("/someJson", func(c *gin.Context) {c.JSON(200, gin.H{"message": "someJson", "status": 200})})//结构体响应r.GET("/someStruct", func(c *gin.Context) {var msg struct {Name     stringMesssage stringNumber   int}msg.Number = 123msg.Messsage = "message"msg.Name = "root"c.JSON(200, msg)})//xml 响应r.GET("/someXML", func(c *gin.Context) {c.XML(200, gin.H{"message": "abc"})})// YAML响应r.GET("/someYAML", func(c *gin.Context) {c.YAML(200, gin.H{"name": "zhangsan"})})// protobuf格式,谷歌开发的高效存储读取的工具// 数组?切片?如果自己构建一个传输格式,应该是什么格式?r.GET("/someProtoBuf", func(c *gin.Context) {reps := []int64{int64(1), int64(2)}// 定义数据label := "label"// 传protobuf格式数据data := &protoexample.Test{Label: &label,Reps:  reps,}c.ProtoBuf(200, data)})r.Run(":8080")
}// 同步异步
func Async() {r := gin.Default()// 1.异步r.GET("/long_async", func(c *gin.Context) {// 需要搞一个副本copyContext := c.Copy()// 异步处理go func() {time.Sleep(3 * time.Second)log.Println("异步执行:" + copyContext.Request.URL.Path)}()})// 2.同步r.GET("/long_sync", func(c *gin.Context) {time.Sleep(3 * time.Second)log.Println("同步执行:" + c.Request.URL.Path)})r.Run(":8080")
}// 中间件// 全局中间件 所有请求都经过此中间件// 定义中间
func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()fmt.Println("中间件开始执行了")// 设置变量到Context的key中,可以通过Get()取c.Set("request", "中间件")status := c.Writer.Status()fmt.Println("中间件执行完毕", status)t2 := time.Since(t)fmt.Println("time:", t2)}
}func GinMiddleWare() {r := gin.Default()// 注册中间件r.Use(MiddleWare()){r.GET("/ce", func(c *gin.Context) {// 取值req, _ := c.Get("request")fmt.Println("request:", req)// 页面接收c.JSON(200, gin.H{"request": req})})}r.Run(":8080")
}//Next()方法// 定义中间
func MiddleWareNext() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()fmt.Println("中间件开始执行了")// 设置变量到Context的key中,可以通过Get()取c.Set("request", "中间件")// 执行函数c.Next() //跳出此中间件执行,去执行正常的流程回来再继续执行下面的程序// 中间件执行完后续的一些事情status := c.Writer.Status()fmt.Println("中间件执行完毕", status)t2 := time.Since(t)fmt.Println("time:", t2)}
}func GinMiddleWareNext() {r := gin.Default()r.Use(MiddleWare()){{r.GET("/ce", func(c *gin.Context) {// 取值req, _ := c.Get("request")fmt.Println("request:", req)// 页面接收c.JSON(200, gin.H{"request": req})})}}r.Run(":8080")
}// 局部中间件
func MiddleWarePartition() {r := gin.Default()//局部中间件使用r.GET("/ce", MiddleWareNext(), func(c *gin.Context) {request, _ := c.Get("request")fmt.Println("request:", request)c.JSON(200, gin.H{"request": request})})r.Run(":8080")
}//中间件练习  定义程序计时中间件,然后定义2个路由,执行函数后应该打印统计的执行时间,如下:func timeUsed(c *gin.Context) {start := time.Now()c.Next()since := time.Since(start)fmt.Println("执行时间:", since)
}func MiddleWareTest() {r := gin.Default()r.Use(timeUsed)//r.GET("/", func(c *gin.Context) {//	shopIndexHandler(c)//	shopHomeHandler(c)//})shoppingGroup := r.Group("/shopping"){shoppingGroup.GET("/index", shopIndexHandler)shoppingGroup.GET("/home", shopHomeHandler)}r.Run(":8080")
}func shopIndexHandler(c *gin.Context) {time.Sleep(5 * time.Second)
}func shopHomeHandler(c *gin.Context) {time.Sleep(3 * time.Second)
}// 日志文件
func GinLog() {gin.DisableConsoleColor()// Logging to a file.f, _ := os.Create("gin.log")gin.DefaultWriter = io.MultiWriter(f)// 如果需要同时将日志写入文件和控制台,请使用以下代码。// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.String(200, "pong")})r.Run(":8080")
}//参数验证 用gin框架的数据验证,可以不用解析数据,减少if else,会简洁许多// 结构体验证 与之前的结构体绑定是一样的  注意就是结构体tag写法有区别
// Person ..
type Person struct {//不能为空并且大于10Age      int       `form:"age" binding:"required,gt=10"`Name     string    `form:"name" binding:"required"`Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}func CheckStruct() {r := gin.Default()r.GET("/struct", func(c *gin.Context) {var person Personif err := c.ShouldBind(&person); err != nil {c.String(500, fmt.Sprint(err))return}c.String(200, fmt.Sprintf("%#v", person))})r.Run(":8080")
}//自定义验证
/*对绑定解析到结构体上的参数,自定义验证功能比如我们要对 name 字段做校验,要不能为空,并且不等于 admin ,类似这种需求,就无法 binding 现成的方法需要我们自己验证方法才能实现 官网示例(https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Custom_Functions)这里需要下载引入下 gopkg.in/go-playground/validator.v8
*/
type Person2 struct {Age int `form:"age" binding:"required,gt=10"`// 2、在参数 binding 上使用自定义的校验方法函数注册时候的名称Name    string `form:"name" binding:"NotNullAndAdmin"`Address string `form:"address" binding:"required"`
}// 1、自定义的校验方法
func nameNotNullAndAdmin(fl validator.FieldLevel) bool {value := fl.Field().String()// 字段不能为空,并且不等于 adminreturn value != "" && value != "test"
}func CheckSelf() {r := gin.Default()// 3、将我们自定义的校验方法注册到 validator中if v, ok := binding.Validator.Engine().(*validator.Validate); ok {// 这里的 key 和 fn 可以不一样最终在 struct 使用的是 keyv.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin)}/*curl -X GET "http://127.0.0.1:8080/testing?name=&age=12&address=beijing"curl -X GET "http://127.0.0.1:8080/testing?name=lmh&age=12&address=beijing"curl -X GET "http://127.0.0.1:8080/testing?name=adz&age=12&address=beijing"*/r.GET("/Test", func(c *gin.Context) {var person Personif e := c.ShouldBind(&person); e == nil {c.String(http.StatusOK, "%v", person)} else {c.String(http.StatusOK, "person bind err:%v", e.Error())}})r.Run(":8080")
}//多语言翻译验证
/*
当业务系统对验证信息有特殊需求时,
例如:返回信息需要自定义,手机端返回的信息需要是中文而pc端发挥返回的信息需要时英文,
如何做到请求一个接口满足上述三种情况。
*/
var (Uni      *ut.UniversalTranslatorValidate *validator.Validate
)type User struct {Username string `form:"user_name" validate:"required"`Tagline  string `form:"tag_line" validate:"required,lt=10"`Tagline2 string `form:"tag_line2" validate:"required,gt=1"`
}func CheckLanguages() {en := en.New()zh := zh.New()zh_tw := zh_Hant_TW.New()Uni = ut.New(en, zh, zh_tw)Validate = validator.New()route := gin.Default()route.GET("/gintest", startPage)route.POST("/gintest", startPage)route.Run(":8080")
}
func startPage(c *gin.Context) {//这部分应放到中间件中locale := c.DefaultQuery("locale", "zh")trans, _ := Uni.GetTranslator(locale)switch locale {case "zh":zh_translations.RegisterDefaultTranslations(Validate, trans)breakcase "en":en_translations.RegisterDefaultTranslations(Validate, trans)breakcase "zh_tw":zh_tw_translations.RegisterDefaultTranslations(Validate, trans)breakdefault:zh_translations.RegisterDefaultTranslations(Validate, trans)break}//自定义错误内容Validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details}, func(ut ut.Translator, fe validator.FieldError) string {t, _ := ut.T("required", fe.Field())return t})//这块应该放到公共验证方法中user := User{}c.ShouldBind(&user)fmt.Println(user)err := Validate.Struct(user)if err != nil {errs := err.(validator.ValidationErrors)sliceErrs := []string{}for _, e := range errs {sliceErrs = append(sliceErrs, e.Translate(trans))}c.String(200, fmt.Sprintf("%#v", sliceErrs))}c.String(200, fmt.Sprintf("%#v", "user"))
}//会话控制
/*
Cookie介绍
HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否由同一个客户端发出
Cookie就是解决HTTP协议无状态的方案之一,中文是小甜饼的意思
Cookie实际上就是服务器保存在浏览器上的一段信息。浏览器有了Cookie之后,每次向服务器发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求
Cookie由服务器创建,并发送给浏览器,最终由浏览器保存
Cookie的用途
测试服务端发送cookie给客户端,客户端请求时携带cookie
*//*
Cookie的缺点
不安全,明文
增加带宽消耗
可以被禁用
cookie有上限
*///Cookie的使用  服务端发送cookie给客户端,客户端请求时携带cookiefunc TestCookie() {r := gin.Default()r.GET("cookie", func(c *gin.Context) {// 获取客户端是否携带cookiecookie, err := c.Cookie("key_cookie")if err != nil {cookie = "NotSet"// 给客户端设置cookie//  maxAge int, 单位为秒// path,cookie所在目录// domain string,域名//   secure 是否智能通过https访问// httpOnly bool  是否允许别人通过js获取自己的cookiec.SetCookie("key_cookie", "value_cookie", 60, "/","localhost", false, true)}fmt.Printf("cookie的值是: %s\n", cookie)})r.Run(":8080")
}func AuthMiddleWare() gin.HandlerFunc {return func(c *gin.Context) {// 获取客户端cookie并校验if cookie, err := c.Cookie("abc"); err == nil {if cookie == "123" {c.Next()return}}// 返回错误c.JSON(http.StatusUnauthorized, gin.H{"error": "err"})// 若验证不通过,不再调用后续的函数处理c.Abort()return}
}func CookieAuth() {r := gin.Default()r.GET("/login", func(c *gin.Context) {// 设置cookiec.SetCookie("abc", "123", 60, "/","localhost", false, true)// 返回信息c.String(200, "Login success!")})r.GET("/home", AuthMiddleWare(), func(c *gin.Context) {c.JSON(200, gin.H{"data": "home"})})r.Run(":8080")
}//Sessions
/*
gorilla/sessions为自定义session后端提供cookie和文件系统session以及基础结构。
主要功能是:
简单的API:将其用作设置签名(以及可选的加密)cookie的简便方法。
内置的后端可将session存储在cookie或文件系统中。
Flash消息:一直持续读取的session值。
切换session持久性(又称“记住我”)和设置其他属性的便捷方法。
旋转身份验证和加密密钥的机制。
每个请求有多个session,即使使用不同的后端也是如此。
自定义session后端的接口和基础结构:可以使用通用API检索并批量保存来自不同商店的session。
*/// 初始化一个cookie存储对象
// something-very-secret应该是一个你自己的密匙,只要不被别人知道就行
var store = sessions.NewCookieStore([]byte("something-very-secret"))func GinSession() {http.HandleFunc("/save", SaveSession)http.HandleFunc("/get", GetSession)err := http.ListenAndServe(":8080", nil)if err != nil {fmt.Println("HTTP server failed,err:", err)return}
}func SaveSession(w http.ResponseWriter, r *http.Request) {// Get a session. We're ignoring the error resulted from decoding an// existing session: Get() always returns a session, even if empty.// 获取一个session对象,session-name是session的名字session, err := store.Get(r, "session-name")if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}// 在session中存储值session.Values["foo"] = "bar"session.Values[42] = 43// 保存更改session.Save(r, w)// 删除// 将session的最大存储时间设置为小于零的数即为删除//session.Options.MaxAge = -1//session.Save(r, w)
}
func GetSession(w http.ResponseWriter, r *http.Request) {session, err := store.Get(r, "session-name")if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}foo := session.Values["foo"]fmt.Println(foo)
}

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

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

相关文章

博弈论 C++

前置知识 若一个游戏满足&#xff1a; 由两名玩家交替行动在游戏进行的任意时刻&#xff0c;可以执行的合法行动与轮到哪位玩家无关不能行动的玩家判负 则称该游戏为一个公平组合游戏。 尼姆游戏&#xff08;NIM&#xff09;属于公平组合游戏&#xff0c;但常见的棋类游戏&…

ASP.NET Core开发Chatbot API

本文介绍基于ASP.NET Core的Chatbot Restful API开发&#xff0c;通过调用大语言模型的SDK&#xff0c;完成一个简单的示例。并且通过容器化进行部署. 安装 首先需要安装.NET环境&#xff0c;笔者在Ubuntu 22.04通过二进制包进行安装&#xff0c;Windows和Mac下都有installer…

终止,半成收入来自海外,收入可持续性被质疑

芬尼科技终止原因如下&#xff1a;芬尼科技4年期间经历了两次IPO失败&#xff0c;公司半成收入来自海外&#xff0c;然而公司泳池收入面临欧洲地区冲突冲击及德国新节能措施影响。交易所质疑其收入是否具有可持续性。 作者&#xff1a;Eric 来源&#xff1a;IPO魔女 9月25日&a…

使用HIP和OpenMP卸载的Jacobi求解器

Jacobi Solver with HIP and OpenMP offloading — ROCm Blogs (amd.com) 作者&#xff1a;Asitav Mishra, Rajat Arora, Justin Chang 发布日期&#xff1a;2023年9月15日 Jacobi方法作为求解偏微分方程&#xff08;PDE&#xff09;的基本迭代线性求解器在高性能计算&#xff…

Webserver(2)GCC

目录 安装GCCVScode远程连接到虚拟机编写代码gcc编译过程gcc与g的区别Xftp连接虚拟机上传文件 安装GCC sudo apt install gcc g查看版本是7.5 touch test.c创建代码 但是在虚拟机中写代码很不方便 VScode远程连接到虚拟机编写代码 gcc test.c -o app在虚拟机中用gcc编译的…

AtCoder ABC376A-D题解

个人觉得 ABC 变得越来越难了/kk/kk/kk 比赛链接:ABC376 Problem A: Code #include <bits/stdc.h> using namespace std; int main(){int N,C;cin>>N>>C;for(int i1;i<N;i)cin>>T[i];int ans0,pre-1e5;for(int i1;i<N;i){if(T[i]-pre>C){…

APP专项测试-冷启动-流量-电量-内存

1、响应时间 1.1怎么获取冷启动时间&#xff08;热启动&#xff0c;就是后台不关后台再次打开&#xff09; 方法一 1.2怎么获取包名 与 启动页 方法三soloPi&#xff1a;启动时间(用户角度出发&#xff0c;页面差异进行计算时间)&#xff1a; 然后默认配置。点击开始录制 1开…

未来汽车驾驶还会有趣吗?车辆动力学系统简史

未来汽车驾驶还会有趣吗&#xff1f;车辆动力学系统简史 本篇文章来源&#xff1a;Schmidt, F., Knig, L. (2020). Will driving still be fun in the future? Vehicle dynamics systems through the ages. In: Pfeffer, P. (eds) 10th International Munich Chassis Symposiu…

高边坡稳定安全监测预警系统解决方案

一、项目背景 高边坡的滑坡和崩塌是一种常见的自然地质灾害&#xff0c;一但发生而没有提前预告将给人民的生命财产和社会危害产生严重影响。对高边坡可能产生的灾害提前预警、必将有利于决策者采取应对措施、减少和降低灾害造成的损失。现有的高边坡监测技术有人工巡查和利用测…

python3的基本数据类型:字符串的其他操作

一. 简介 前面文章学习了 python3 中字符串的创建&#xff0c;连接与转化。文章如下&#xff1a; 本文继续来学习 python3 中字符串的其他操作。 二. python3 的基本数据类型&#xff1a;字符串的其他操作 1. 字符转义 python3 中使用反斜杠 \ 转义特殊字符&#xff0c;它…

Docker 搭建mysql

拉取mysql镜像 docker pull mysql # 拉取镜像 [rooteason ~]# docker pull mysql Using default tag: latest latest: Pulling from library/mysql 72a69066d2fe: Pull complete 93619dbc5b36: Pull complete 99da31dd6142: Pull complete 626033c43d70: Pull complete 37d…

JavaEE----多线程(二)

文章目录 1.进程的状态2.线程的安全引入3.线程安全的问题产生原因4.synchronized关键字的引入4.1修饰代码块4.2修饰实例方法4.3修饰静态方法4.4对象头介绍4.5死锁-可重入的特性 5.关于死锁的分析总结5.1死锁的分析5.2死锁成因的必要条件5.3死锁的解决方案 1.进程的状态 public…

网站被浏览器提示“不安全”,如何快速解决

当网站被浏览器提示“不安全”时&#xff0c;这通常意味着网站存在某些安全隐患&#xff0c;需要立即采取措施进行解决。 一、具体原因如下&#xff1a; 1.如果网站使用的是HTTP协议&#xff0c;应立即升级HTTPS。HTTPS通过使用SSL证书加密来保护数据传输&#xff0c;提高了网…

leetcode 504.七进制数

1.题目要求: 2.题目代码: class Solution { public:string convertToBase7(int num) {string s;int n abs(num);if(num 0){s.push_back(0);return s;}//采用迭代法&#xff0c;对基数进行求余while(n){s.push_back(n % 7 0);n / 7;}if(num < 0){s.push_back(-);}//反转字…

java对接U9C接口

使用java对接U9C的svn接口&#xff0c;说起来也不复杂&#xff0c;走过了路才敢这么说&#xff0c;大致步骤如下&#xff1a; 1.找到接口地址 http://U9地址/Services/***.svc,打开接口地址&#xff0c;如下显示为正常 2.确定需要的参数 接口开发前需要大致了解哪些是必须的…

Flux危,SD 3.5王者归来!个人可以免费商用!

StabilityAI最新发布了Stable Diffusion 3.5&#xff0c;这次公开发布包括多个模型&#xff0c;包括Stable Diffusion 3.5 Large和Stable Diffusion 3.5 Large Turbo。此外&#xff0c;Stable Diffusion 3.5 Medium将于10月29日发布。这些模型因其大小而具有高度的可定制性&…

状态栏黑底白字后如何实现圆角以及固定状态栏

如何实现如下效果: 上述是将状态栏实现黑底白字+圆角+状态栏固定的逻辑 具体代码patch如下: From 6a3b8ed5d3f49a38d8f9d3e488314a66ef5576b8 Mon Sep 17 00:00:00 2001 From: andrew.hu <andrew.hu@quectel.com> Date: Fri, 18 Oct 2024 16:43:49 +0800 Subject: [P…

跟《经济学人》学英文:2024年10月19日这期 Why the world is so animated about anime

Why the world is so animated about anime Japan’s cartoons have conquered its screens, and more 为什么世界对动漫如此热衷 animated&#xff1a;热衷的&#xff1b;生动的&#xff1b;活泼的&#xff1b;活跃的 anime&#xff1a;美 [ˈnɪmeɪ] 动漫 原文&#xff…

Java程序员如何获得高并发经验?

高并发编程 提到并发编程很多人就会头疼了&#xff1b;首先就是一些基础概念&#xff1a;并发&#xff0c;并行&#xff0c;同步&#xff0c;异步&#xff0c;临界区&#xff0c;阻塞&#xff0c;非阻塞还有各种锁全都砸你脸上&#xff0c;随之而来的就是要保证程序运行时关键…

海外著名新闻门户媒体软文发稿之华盛顿独立报-大舍传媒

在当今全球化的时代&#xff0c;信息传播的速度和范围达到了前所未有的程度。对于企业和个人而言&#xff0c;如何在国际舞台上有效地展示自己、传递信息&#xff0c;成为了一项至关重要的任务。而海外媒体发稿&#xff0c;特别是通过像华盛顿独立报这样的知名新闻门户&#xf…