linux 正则查找email_Hello Iris简易微博类App开发教程3-查找用户和用户登录

0afed76f8514e7a4c020408c0f9dba59.png

用户邮箱地址有效性验证

在上一节中,我们编写了用于用户注册的代码。但是当时并没有为用户的Email添加有效性验证,导致Email被设置为任何字符串都能注册成功。所以在本节初,我们首先来为Email添加一个有效性验证。

首先还是来编写测试数据,打开我们上一节创建的test_user_data.json,向其中添加一个测试用的用户信息。

代码清单3-1

models/test_user_data.json

[
.
.
. {"email":"email8@exam_ple.com","password": "password8","username": "username8"}
]

我们添加的这个新的用户信息的Email是一个无效的邮箱地址。接着我们打开user_test.go,也向其中增加一个测试case。

代码清单3-2

models/user_test.go

func TestUserCreate(t *testing.T) {
.
.
.err = users[7].Create() // 测试Email为无效的邮箱地址的情况if err == nil {t.Error("expected get an error but no error occured.")} else if err.Error() != "Invalid email address" {t.Errorf("expected get "Invalid email address" but got "%s"n", err.Error())}
}

然后打开user.go,我们向其中增加一个isValidEmail()方法用来检测设置的用户Email是否有效,然后再在向数据库插入数据之前,检查邮箱地址是否有效。如果无效的话,我们返回一个“Invalid email address”的错误。

代码清单3-3

models/user.go

package modelsimport ("errors""regexp""time"
)type User struct {ID intEmail *string `gorm:"not null;unique_index"`Password *string `gorm:"not null"`Username *string `gorm:"not null;unique_index"`Message stringCreatedAt time.TimeUpdatedAt time.Time
}func (u *User) Create() (err error) {if u.Email != nil && !u.isValidEmail() {err = errors.New("Invalid email address")return err}if u.Password == nil {err = errors.New("Error occured when creating user")return err}plain := *u.Passwordencrypt := Encrypt(plain)u.Password = &encrypterr = DB.Create(&u).Errorif err != nil {err = errors.New("Error occured when creating user")}return
}func (u *User) isValidEmail() bool {pat := `(?i)A[w+-.]+@[a-zd-.]+.[a-z]+z`email := u.Emailok, _ := regexp.MatchString(pat, *email)return ok
}

在isValidEmail()方法里,我们使用了正则表达式去匹配Email属性。如果能匹配上,则返回true,否则就会返回false。所使用的正则表达式的含义如下:

fadb739c6ed09dcf3274fd737362868b.png

完成后我们新打开一个命令行工具,cd到models文件夹下,运行go test

C:Userssxu37GosrcGoWeb>cd models
C:Userssxu37GosrcGoWebmodels>go test

结果如下:

PASS
ok GoWeb/models 4.101s

测试Pass,说明我们添加的用于测试邮箱地址是否有效的代码是正确的。

查找用户

既然我们已经有了创建用户的功能,接下来,我们就来实现简单的用户查找的功能。在创建用户模型的时候,我们在数据库中,将用户的id列设置为了primary key,并且在email和username都创建了index,这就意味着,通过ID、Email和Username这三者我们都可以快速检索到一个用户的信息。

首先我们还是来编写查找用户的测试代码。打开user_test.go,向其中增加下面三个函数。

代码清单3-4

models/user_test

.
.
.
func TestFindUserByID(t *testing.T) {id1, id2 := 5, 4user1, err := FindUserByID(id1)if err == nil {t.Error("expected find no user but got:", user1)}user2, err := FindUserByID(id2)if err != nil {t.Error("got an unexpected error:", err)} else if user2.ID != id2 {t.Errorf("expected find user which id is %d but got user which id is: %dn", id2, user2.ID)} else if user2.Email != nil || user2.Password != nil {t.Error("expected do not get user email and password but got them now")}
}func TestFindUserByEmail(t *testing.T) {email1, email2 := "email6@exam.com", "email5@exam.com"user1, err := FindUserByEmail(email1)if err == nil {t.Error("expected find no user but got:", user1)}user2, err := FindUserByEmail(email2)if err != nil {t.Error("got an unexpected error:", err)} else if *user2.Email != email2 {t.Errorf("expected find user which email is "%s" but got user which email is: "%s"", email2, *user2.Email)}
}func TestFindUserByUsername(t *testing.T) {username1, username2 := "username6", "username5"user1, err := FindUserByUsername(username1)if err == nil {t.Error("expected find no user but got:", user1)}user2, err := FindUserByUsername(username2)if err != nil {t.Error("got an unexpected error:", err)} else if *user2.Username != username2 {t.Errorf("expected find user which username is "%s" but got user which username is: "%s"", username2, *user2.Username)} else if user2.Email != nil || user2.Password != nil {t.Error("expected do not get user email and password but got them now")}
}

在上面的测试代码中,我们分别测试了通过ID、Email和Username来查找用户的情况。因为这三个属性在数据库中都是唯一的,所以我们每条测试case最多只能找到一条数据。所以我们只测试了找到的这条数据的某一项值是否是我们所期待的值。如果是,就表明这条数据就是我们需要的数据,如果不是,说明我们查找用户的功能代码有问题。我们还检查了查找到的数据中是否包含用户的Email和Password,如果包含了的话也不能通过测试。

接下来,我们就来编写查找用户的代码。因为在查找用户之前,我们通常不会事先得到一个用户的实例,所以我们直接将查找用户的代码作为函数来编写的。另外,我们为FindUserByID()和FindUserByUsername()添加了Select()函数,对查询到的数据进行筛选,不从数据库中获取用户的email和password信息,以保证用户账号的安全。

代码清单3-5

models/user.go

.
.
.
var QueryKey string = "id, username, message"
func FindUserByID(id int) (user User, err error) {DB.Where("id = ?", id).Select(QueryKey).Find(&user)if user.ID == 0 {err = errors.New("Cannot find such user")}return
}func FindUserByEmail(email string) (user User, err error) {DB.Where("email = ?", email).Find(&user)if user.ID == 0 {err = errors.New("Cannot find such user")}return
}func FindUserByUsername(username string) (user User, err error) {DB.Where("username = ?", username).Select(QueryKey).Find(&user)if user.ID == 0 {err = errors.New("Cannot find such user")}return
}

清空数据库,然后打开命令行,cd到models目录下,运行go test。

C:Userssxu37GosrcGoWebmodels>go test
.
.
.
PASS
ok GoWeb/models 3.781s

接下来,我们为查找用户编写一个接口。在我们的App中,我们不会将FindUserByEmail()公开。这个函数将只在App内部被使用。FindUserByUsername()可以作为通过用户名查找用户的功能被公开,但是我们目前并不打算先实现这个功能。我们首先实现通过ID来查找用户,然后我们通过访问类似“users/1”的路径来获取某个具体的用户的信息。我们先编写测试代码。向users_controller_test.go中增加如下的测试代码:

代码清单3-6

controllers/users_controller_test.go

func TestFindUserByID(t *testing.T) {id := 1controller := UsersController{}user, err := controller.Show(id)if user.ID == 0 || err != nil {t.Error("expected to show user but error occured:", user, err)}
}

这个测试case很简单,就是指定id为1,看能不能返回id为1的用户。

接着我们就应用代码,应用代码也很简单。

代码清单3-7

controllers/users_controller.go

func (c *UsersController) BeforeActivation(b mvc.BeforeActivation) {middleware := func(ctx iris.Context) {ctx.Application().Logger()ctx.Next()}b.Handle("POST", "/users/new", "Create", middleware)b.Handle("GET", "/users/{id:int}", "Show", middleware)
}func (c *UsersController) Create(ctx iris.Context) (user models.User, err error) {...
}func (c *UsersController) Show(id int) (user models.User, err error) {user, err = models.FindUserByID(id)return
}

注意在设置路径的时候,我们用/users/{id:int}的形式设置了一个动态路径。int值会通过{id:int}作为id参数传入到Show()方法中。加入我们访问/users/1这个路径,那么访问就会被转发到Show()方法并且参数为1,即Show(1)。

清空数据库,cd到controllers目录,运行go test

C:Userssxu37GosrcGoWebcontrollers>go test
.
.
.
PASS
ok GoWeb/controllers 3.124s

接着,我们再试一试接口。

代码清单3-8

main_test.go

.
.
.func TestUsersShowRoute(t *testing.T) {app := weiboApp()e := httptest.New(t, app)request := e.Request("GET", "/users/1")response := request.Expect()response.Status(httptest.StatusOK)
}
C:Userssxu37GosrcGoWeb>go test
.
.
.
PASS
ok GoWeb 2.655s

现在我们不仅能创建用户,还能根据用户的ID查询一个用户。查询到用户之后,我们可以实现对这些用户的资料进行更新和删除。

但是仅仅这样操作就会产生一个问题,任何用户,只要知道其他用户的ID,就可以随意地向服务器发送请求,去更改、删除其他用户的信息。这明显是很不安全的,所以我们需要为更新和删除操作增加一点安全性。大多数App在进行更新和删除操作的时候都需要有先进行登录,有些严格的App还需要登录的用户拥有这些操作的权限才可以。在这里,我们实现只要用户登录就能对自己的账号进行更新和删除。

用户登录

那么接下来,我们来实现用户登录。用户登录功能的实现最简单的方式就是利用session,通过下面几个步骤来实现:

1. 客户端通过request将用户的邮箱和密码提交到服务器。

2. 服务器取得request的数据后,会首先从数据库读取该用户的信息,并判断数据库和request提交上来的信息是否一致。

3. 如果一致的话,服务器将该用户的信息记录到session里,并将session的标识返回给客户端。

4. 客户端收到session的标识之后将它添加到之后每一次request的header里,服务器通过识别request的header里的session标识,来判断该客户端是否已经登录。

一般还会给服务器的session 设置一个有效期,一旦某个session生成后超过了一个固定期限即视为过期,过期的session也是无效的。

在上面的登录过程中,我们User模型需要做的就是进行登录验证:对客户端request过来的邮箱和密码进行验证,并返回一个布尔值来表示验证结果是否通过。首先我们来编写登录验证的测试代码。打开models文件夹,新建一个test_auth_data.json,然后将下列测试数据保存到该文件中。

代码清单3-9

models/test_auth_data.json

[{"email": "wrongemail4@example.com","password": "password4"},{"email": "email4@exam.com","password": "wrongpassword4"},{"email": "email4@exam.com","password": "password4"}
]

然后打开test_user.go,稍微修改一下setup()函数,然后向其中增加一个TestUserAuthenticate()函数。

代码清单3-10

models/test_user.go

.
.
.
func setup(filename string) (users []User) {file, _ := os.Open(filename)defer file.Close()data, _ := ioutil.ReadAll(file)json.Unmarshal(data, &users)return users
}func TestUserCreate(t *testing.T) {users := setup("test_user_data.json")  
.
.
.}func TestUserAuthenticate(t *testing.T) {users := setup("test_auth_data.json")if _, err := users[0].Authenticate(); err == nil {if err.Error() != "Invalid email or password" {t.Errorf("expected get "Invalid email or password" but got "%s"n", err.Error())}t.Error("expected authentication fail but it passed")}if _, err := users[1].Authenticate(); err == nil {if err.Error() != "Invalid email or password" {t.Errorf("expected get "Invalid email or password" but got "%s"n", err.Error())}t.Error("expected authentication fail but it passed")}if user, err := users[2].Authenticate(); err != nil {t.Error("expected authentication pass but it failed cause:", err)} else if user.ID != 3 {t.Errorf("expected user id to be 3 but got %dn", user.ID)}}

在这份测试代码中,我们分别测试了三种情况:email不正确、password不正确和两者都正确。

前两种情况,我们除了验证不通过以外,我们还得到了一个Invalid email or password的错误信息。只有email和password都正确的时候,我们确认验证通过,此时错误为空。

接下来,我们就为User模型添加Authenticate()方法。

代码清单3-11

models/user.go

.
.
.
func (u *User) Authenticate() (user User, err error) {user, err = FindUserByEmail(*u.Email)if err != nil {return}if user.ID == 0 || *user.Password != Encrypt(*u.Password) {user = User{}err = errors.New("Invalid email or password")return}return
}

登录验证的代码中,我们通过FindUserByEmail()函数寻找数据库中email和待验证的用户的Email一致的数据。将找到的数据的password和待验证的用户的Password进行比较。如果找不到用户,或者找到的记录和待验证的用户两者的password不一致,那么我们就判定为验证失败,并返回一个Invalid email or password错误。如果既能找到记录,而且password也一致,我们就判定为验证通过。

登录验证的代码就编写完成,就可以进行测试了。我们首先还是清空数据库:

DROP TABLE users;

然后打开命令行工具,cd到models目录下,运行go test。

C:Userssxu37GosrcGoWebmodels>go test
.
.
.
PASS
ok GoWeb/models 4.417s

说明我们登录验证的代码的行为符合预期。

接下来,我们还需要在控制器里面添加一个Login()方法,我们希望用这个Login()方法来处理用户登录。如果用户登录验证成功,Login()方法会返回登录成功的用户信息。如果登录验证失败,我们就返回从User#Authenticate()得到的错误。

首先我们还是来编写测试代码。打开users_controller_test.go,向里面添加下面的函数。

代码清单3-12

controllers/users_controller_test.go

.
.
.
func TestUserLogin(t *testing.T) {app := iris.New()ctx := context.NewContext(app)// 向新创建的ctx中添加一个ResponseWriter用来写入session的信息w := context.AcquireResponseWriter()hw := httptest.NewRecorder()w.BeginResponse(hw)ctx.ResetResponseWriter(w)// 向新创建的ctx中添加一个Request并将文件中的数据读取到Request的Body中file, _ := os.Open("sample_login_user.json")defer file.Close()newRequest, _ := http.NewRequest("POST", "/login", nil)newRequest.ContentLength = 500newRequest.Body = filectx.ResetRequest(newRequest)// 创建一个UsersController的实例,并设置该实例的Session属性controller := UsersController{}cookie := http.Cookie{Name: "sample_cookie_uuid", Value: ""}ctx.SetCookie(&cookie)sess := sessions.New(sessions.Config{Cookie: "weibo_app_cookie"})controller.Session = sess.Start(ctx)// 调用UsersController实例的Login()方法进行测试user, err := controller.Login(ctx)if err != nil {t.Error("expected no error, but an error occured:", err)}if user.ID != 1 {t.Errorf("expected returned user id to be 1, but got %dn:", user.ID)}id, _ := controller.Session.GetInt("userID")if id != 1 {t.Errorf("expected user id in session to be 1, but got %dn", id)}
}

在上面的代码中,我们会从一个叫sample_login_user.json中读取测试数据,然后用测试数据作为参数调用Login()方法模拟登录。登录后,我们首先检查登录过程有无err以及被登录的用户是否是我们的测试用户,接着我们访问Session,并用GetInt获得Session里的userID字段。检查Session的userID字段的值是否和测试数据的id是一致的。

接着我们还是在controllers目录下,新建一个sample_login_user.json文件,用来编写测试数据。

代码清单3-13

controllers/sample_login_user.json

{"email": "email1@sample.com","password": "password1"
}

接下来,我们打开users_controller.go,为UsersController新增一个Session属性和一个Login()方法:

代码清单3-14

controllers/users_controller.go

package controllersimport ("goweb/models""github.com/kataras/iris""github.com/kataras/iris/mvc""github.com/kataras/iris/sessions"
)type UsersController struct {Session *sessions.Session
}
.
.
.
func (c *UsersController) Login(ctx iris.Context) (user models.User, err error) {if err = ctx.ReadJSON(&user); err != nil {ctx.StatusCode(iris.StatusBadRequest)return}if user, err = user.Authenticate(); err != nil {return}c.Session.Set("userID", user.ID)return
}

在Login()方法中,我们读取客户端request中的json格式的数据,然后将这些数据映射为一个User实例。调用这个User实例的Authenticate()方法进行验证,如果验证能够通过,我们就将该用户的ID记录到session里,并且将验证通过的用户保存到前面我们声明的全局变量里。

接下来,我们在main.go里将session注册到WeiboApp里。

代码清单3-15

main.go

package mainimport ("github.com/kataras/iris""github.com/kataras/iris/middleware/logger""github.com/kataras/iris/middleware/recover""github.com/kataras/iris/mvc""github.com/kataras/iris/sessions""goweb/controllers"
)func main() {app := weiboApp()app.Run(iris.Addr(":8080"))
}func weiboApp() *iris.Application {app := iris.New()app.Use(recover.New())app.Use(logger.New())weiboApp := mvc.New(app)expiresTime, _ := time.ParseDuration("168h")sess := sessions.New(sessions.Config{Cookie: "weibo_app_cookie", Expires: expiresTime})weiboApp.Register(sess.Start,)helloWorldController := new(controllers.HelloWorldController)usersController := new(controllers.UsersController)weiboApp.Handle(helloWorldController)weiboApp.Handle(usersController)return app
}

我们在创建session的时候,设置的session的名字“weibo_app_cookie”和过期的时间168个小时,即一个星期。

接下来,我们清空数据库,打开命令行工具,cd到controllers目录,运行go test

C:Userssxu37Go>cd srcgowebcontrollers
C:Userssxu37GosrcGoWebcontrollers>go test
.
.
.
PASS
ok GoWeb/controllers 8.059s

说明我们用户登录的代码也是编写正确的。接着我们只需要再测试一下”/login“这个路径是否可以用”POST“方法访问即可。

代码清单3-16

main_test.go

.
.
.
func TestLoginRoute(t *testing.T) {app := weiboApp()e := httptest.New(t, app)request := e.Request("POST", "/login")request.WithJSON(map[string]interface{}{"email": "email1@example.com", "password": "password1"})response := request.Expect()response.Status(httptest.StatusOK)
}

清空数据库,然后打开命令行工具,cd到项目根目录下,运行go test

C:Userssxu37GosrcGoWeb>go test
.
.
.
PASS
ok GoWeb 3.684s

用户登录的功能我们也成功实现了。 我们也还是可以使用crul命令测试一下:

C:Userssxu37GosrcGoWeb>curl -i -X POST -d {""email"":""email1@example.co
m"",""password"":""password1""} “http://localhost:8080/login”

1bda7156e2e7b66369fe2a74b09279dd.png

服务器按照预期返回了登录成功的用户信息。

在开始下一节之前,我们还是先把代码push到Github上保管。

C:Userssxu37GosrcGoWeb>git add –A
C:Userssxu37GosrcGoWeb>git commit -m "user login"
C:Userssxu37GosrcGoWeb>git push

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

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

相关文章

原生的html组件,如何创建HTML5与原生UI组件混合的移动应用程序

本文将介绍如何使用Trigger.io创建原生的顶部栏、标签栏、以及HTML/CSS/JavaScript的混合型移动应用程序。以后我们将添加更多的原生UI组件到Trigger.io,但现在你只需要使用web技术就可以在IOS和Android上创建漂亮而流畅的移动应用。这是一个简单的菜谱应用程序的屏…

c语言不会可以学好java吗_有人说学了C语言,两天就能学会Java,两个星期就可以找工作?...

作为一个过来人来说,编程如果真的那么简单就不会导致现在各大公司还喊着招不到人的情况了,虽然编程领域里面有触类旁通的说法,但这个说法只是针对于对于一种编程已经掌握到一定程度了,不是简单的学过或者做过就可以轻松的转向别的…

wps合并所有sheet页_WPS里面如何批量打印(WPS2019)

WPS里面如何批量打印(WPS2019)分四步:1. 创建WPS表格获奖名单2. 创建WPS文字的获奖模板3. 将WPS表格名单内容调入到WPS文字奖状模板中4. 批量打印(或保存)(注:我在这里用了别人的office版本的表…

keepalived mysql双主架构图_基于MySQL双主的高可用解决方案理论及实践

MySQL在互联网应用中已经遍地开花,但是在银行系统中,还在生根发芽的阶段。本文记录的是根据某生产系统实际需求,对数据库高可用方案从需求、各高可用技术特点对比、实施、测试等过程进行整理,完善Mysql高可用方案,同时…

小爱同学100个奇葩回复_小米小爱音箱Pro开箱评测,看到的不仅是全面升级更是小米loT的高速发展...

Hello,大家好,这里是科技能量站,今天有何大家见面了,本期小编给大家带来的是小米最新推出的小爱同学“小米小爱音箱Pro”,小米在前两年推出了自家的小米AI音箱,那一刻起,智能语音人工助手在小米…

你了解的继承方式html,法定继承、遗嘱继承、遗赠,这三种房产过户方式你了解多少?...

在房产过户的中,与继承相关的方式有三种:法定继承、遗嘱继承和遗赠。这三种过户方式其实是有较大不同的,下面我们就来具体分析下,希望能对需要的朋友有帮助。过户在房产过户中,与继承相关的有三种,分别是法…

wedo2.0编程模块介绍_福特福克斯TCM重新编程操作

适用范围支持车型/年款:新福克斯 2011 – 2018 1.6L/2.0L DPS6变速箱 车型嘉年华 2013 1.5L DPS6变速箱 车型翼博 2013 – 2016 1.5L DPS6变速箱 车型功能介绍升级ECU版本或者对空白ECU写入数据, PAD Ⅲ请使用有线连接进行编程, 测试时保证车辆电瓶电压充足条件要求…

必须重启计算机才能关闭用户账户控制,Win10系统怎么彻底关闭用户帐户控制?...

大多是win10系统用户都知道,微软所推出的用户帐户控制能够限制一些病毒程序启动,从而较好的保护我们的电脑安全,以达到降低win10系统中毒的风险。可是每当我们运行一个程序的时候系统总会出现提示,感觉很烦,那么怎么永…

三菱plcfx5u指令手册_从西门子200的PLC程序来看三菱FX5U的PLC程序

小型PLC系统中西门子200系列和三菱的FX系列是应用的比较多的,作为工控行业的工程师是很有必要明白他们之间的一些不同点的,特别是从程序上来说,因此来写一篇文章,简单的聊聊他们在编程上的一下不同!西门子PLC和三菱PLC…

cron 每10分钟执行一次_早餐儿子最爱它,简单卷一卷,10分钟做一大盘,三天两头吃一次...

早餐儿子最爱它,卷一卷特简单,10分钟做一大盘,三天两头吃一次。俗话说得好“早餐要吃好,午餐要吃饱,晚餐要吃少。”可见,早餐的重要性,一般早餐在一日三餐中占30%的热量,碳水化合物、…

计算机刚过国家线能调剂到哪些学校,2020考研:刚过国家线好不好调剂?这4个调剂策略!考生要知道...

原标题:2020考研:刚过国家线好不好调剂?这4个调剂策略!考生要知道近日,2020年全国硕士研究生基本分数线已经公布。对于340万左右的考研人来说,心中的那块石头终于有了着落,不管考得好或者不好&a…

生命银行怎么样_减脂就像是从“脂肪银行”中提款,想要成功,你要做到这两点...

本期内容,我们来深刻的聊一聊减脂,减肥。食物为什么会让你长胖?怎样快速的瘦下来?1 食物能量的储存人是铁,饭是钢,一天不吃饿得慌!食物是维持人体正常运转的唯一能量来源,被我们吃进…

二次开发_企业ERP系统二次开发问题的探讨分析

新朋友点上方蓝字“ERP之家”快速关注导读:根据笔者多年在企业中实施及推进ERP,概的实战经验,阐述了ERP系统二次开发的必然性和二次发开的成因及存在的风险,提出如何合理地规避二次开发所带来的风险,值得为实施ERP系统…

西门子逻辑运算指令_西门子S7-200 SMART逻辑运算指令应用实例

本篇我们通过一个实例来讲解一下西门子S7-200 SMART逻辑运算指令在程序中的使用。要求将VW2和VW4中的数据进行逻辑与后,送到VW6。首先我们双击电脑桌面上的STEP7-Micro/WIN SMART图标,打开编程软件,从指令树的位逻辑中选择一个常开触点拖放到…

强行终止python_中国的真实离婚率:一点也不高,反而低的惊人 | 用python计算离婚率...

3600字。现在的网络上,似乎普遍有一种共识,那就是中国的离婚率高的吓人。 各种耸人听闻的标题,比如什么“北上广深等一线城市离婚率高达40%”、“天津的离婚率高达70%”……搞得人心惶惶。有网友对此提出质疑,说,我怎么…

单片机拼字程序怎么做_餐饮怎么用微信小程序?餐饮行业怎么做小程序

随着移动互联网的发展,互联网餐饮这种新型餐饮模式已经发展到了很成熟的地步。很多餐厅都会觉得,自己的活动很有吸引力,但是推广力度不够,不能迅速营造人气,提升收益。对于餐饮人来说,微信是主要的传播渠道…

测试金士顿固态硬盘软件,金士顿固态硬盘优化工具(Kingston Toolbox)

金士顿固态硬盘优化工具KingstonToolbox是金士顿官方出品的SSD优化工具,通过软件能够对金士顿的固态硬盘进行优化设置,同时软件还支持对硬盘进行固件升级,有需要的可以下载使用。金士顿固态硬盘优化工具(Kingston Toolbox)是金士顿官方出品的…

绝地求生测试服画面优化软件,绝地求生正式服设置详解 教你调校最完美的画质...

在绝地求生新版本正式上线之后,蓝洞对于绝地求生的两张地图都做了比较大的优化,令低配玩家的游戏帧数都有了比较大的提升,而此前普遍反映的掉帧问题也得到了不小的改善。不过,由于游戏自身的原因(场景较大,玩家较多&am…

小学计算机画线反思,小学《我们身边的线条》教学反思

小学《我们身边的线条》教学反思《我们身边的线条》教学反思线条课从第一册开始,一直贯穿整个小学美术课程,由简到繁,由易到难。《我们身边的线条》一课,就是要引导学生发现生活中到处都有的线条。线条是绘画造型的基本元素&#…

.net 实时通信_【WebSocket】实时多人答题对战游戏

本文公众号来源:后端技术漫谈 作者:蛮三刀把刀前言前两章教程,我们使用WebSocket的基础特性打造了一个小小聊天室,并在第二章对其进行了集群化改造。系列教程回顾:手把手搭建WebSocket多人在线聊天室【多人聊天室】Web…