mangokit:golang web项目管理工具,使用proto定义http路由和错误
文章目录
- 前言
- 1、mangokit介绍
- 1.1 根据proto文件生成http路由
- 1.2 根据proto文件生成响应码
- 1.3 使用wire来管理依赖注入
- 2、mangokit实现
- 2.1 protobuf插件开发
- 2.2 mangokit工具
- 3、使用示例
- 3.1 创建新项目
- 3.2 添加新的proto文件
- 3.3 代码生成
前言
在使用gin框架开发web应用时,需要我们自己手动完成请求到结构体的反序列化,以及发送响应,如下:
func Handler(ctx *gin.Context) {user := new(User)if err := ctx.ShouldBind(&user); err != nil {...}...resp := serivce()...ctx.Json(http.StatusOk, resp)
}
显然,这些工作都是多余的,和业务无关的,每个handler都需要我们自己处理,非常的麻烦
为了解决这个问题,我们可以使用反射的方式来字段完成请求数据到结构体的映射;对于响应,则定义一个统一的结构体,并且让handler返回这个结构体,如下:
type Response struct {R RespValueStatus int
}type RespValue struct {Data interface{} `json:"data"`Codee int `json:"code"`Messagee string `json:"message"`
}func NewResponse(status, code int, message string, data interface{}) *Response {...
}
func Handler(ctx *gin.Context, user *User) *Response {...resp = service()...return NewResponse(http.StatusOk, 0, "success", resp)
}
在注册路由时,则需要使用反射来对我们的handler进行适配,使用反射机制创建请求参数,然后将数据反序列化为对应的结构体,然后调用我们定义的handler,并且获取到返回值,调用ctx.Json来发送
这种方式方便了我们的开发,但是使用反射会对程序带来一定的性能损失(但是在这里只是简单的使用,性能损失很少),并且使用反射容易出现错误
最近在使用了bilibili的kratos框架后,给了我一些灵感,我们完全可以使用proto来定义http的路由,然后生成反序列化的结构代码,并且可以使用proto来定义返回错误码等。
因此借鉴了kratos的设计,我实现了一个小工具用来加速我的web开发
github:https://github.com/mangohow/mangokit
1、mangokit介绍
mangokit是一个web项目的管理工具,它的功能如下:
- 根据预设的项目结构创建出一个web项目,使用已有的代码框架,减少工作量
- 使用proto来定义http路由以及错误码,使用相关工具生成代码,完成自动结构体反序列化以及返回值响应
- 使用wire来管理依赖注入,减少依赖管理的烦恼
1.1 根据proto文件生成http路由
proto定义文件如下:
hello.proto
syntax = "proto3";package hello.v1;import "google/api/annotations.proto";option go_package = "api/helloworld/v1;v1";// 定义service
service Hello {rpc SayHello (HelloRequest) returns (HelloReply) {option (google.api.http) = {get: "/hello/:name"};}
}message HelloRequest {string name = 1;
}message HelloResponse {string message = 2;
}
然后使用mangokit命令根据proto生成gin框架对应的路由处理器:
mangokit generate proto api
生成的文件如下:
hello.pb.go
hello_http_gin.pb.go
其中hello.pb.go
是protoc --go-out生成的,而hello_http_gin.pb.go
是我们自己写的proto插件protoc-gen-go-gin
生成的
hello_http_gin.pb.go
的代码如下:
// Code generated by protoc-gen-go-gin. DO NOT EDIT.
// versions:
// - protoc-gen-go-gin v1.0.0
// - protoc v3.20.1
// source: helloworld/v1/proto/hello.protopackage v1import ("context""net/http""github.com/mangohow/mangokit/serialize""github.com/mangohow/mangokit/transport/httpwrapper"
)type HelloHTTPService interface {SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}func RegisterHelloHTTPService(server *httpwrapper.Server, svc HelloHTTPService) {server.GET("/hello/:name", _Hello_SayHello_HTTP_Handler(svc))
}func _Hello_SayHello_HTTP_Handler(svc HelloHTTPService) httpwrapper.HandlerFunc {return func(ctx *httpwrapper.Context) error {in := new(HelloRequest)if err := ctx.BindRequest(in); err != nil {return err}value := context.WithValue(context.Background(), "gin-ctx", ctx)reply, err := svc.SayHello(value, in)if err != nil {return err}ctx.JSON(http.StatusOK, serialize.Response{Data: reply})return nil}
}
在上面生成的go代码中,包含一个接口的定义,其中包含了我们定义的handler方法
并且提供了RegisterHelloHTTPService
函数来注册路由,注册的路由为_Hello_SayHello_HTTP_Handler
函数,在这个函数中有反序列化的代码,以及响应代码
因此我们只需要实现HelloHTTPService
中的方法,并且调用RegisterHelloHTTPService
来注册路由即可,大大的减少了我们的工作量。
这有点类似于grpc的方式。
1.2 根据proto文件生成响应码
有时候只使用http的状态码是不够的,比如200表示请求成功,但是虽然请求成功了,还可能出现其它问题。
比如一个登录的接口,用户登录时可能出现以下的情况:1、用户不存在 2、密码错误 3、用户被封禁了
因此,我们需要定义相关的一些响应码来处理这些情况
proto定义文件如下:
errors.proto
syntax = "proto3";package errors.v1;
import "errors/errors.proto";option go_package = "api/errors/v1;v1";enum ErrorReason {// 设置缺省错误码option (errors.default_code) = 500;Success = 0 [(errors.code) = 200];// 为某个枚举单独设置错误码UserNotFound = 1 [(errors.code) = 200];UserPasswordIncorrect = 2 [(errors.code) = 200];UserBanned = 3 [(errors.code) = 200];
}
在上面的proto文件中,我们使用enum来定义响应码,其中包括int
类型的响应码,以及返回的http状态码(errors.code)
然后使用mangokit来生成go代码:
mangokit generate proto api
生成的文件如下:
errors.pb.go
errors_errors.pb.go
其中errors.pb.go
是protoc --go_out生成的,而errors_errors.pb.go
同样也是自己编写的proto插件protoc-gen-go-error
生成的
errors_errors.pb.go
中的代码如下:
// Code generated by protoc-gen-go-error. DO NOT EDIT.
// versions:
// - protoc-gen-go-error v1.0.0
// - protoc v3.20.1
// source: errors/v1/proto/errors.protopackage v1import ("fmt""github.com/mangohow/mangokit/errors"
)func ErrorSuccess(format string, args ...interface{}) errors.Error {return errors.New(int32(ErrorReason_Success), 200, ErrorReason_Success.String(), fmt.Sprintf(format, args...))
}// 为某个枚举单独设置错误码
func ErrorUserNotFound(format string, args ...interface{}) errors.Error {return errors.New(int32(ErrorReason_UserNotFound), 200, ErrorReason_UserNotFound.String(), fmt.Sprintf(format, args...))
}func ErrorUserPasswordIncorrect(format string, args ...interface{}) errors.Error {return errors.New(int32(ErrorReason_UserPasswordIncorrect), 200, ErrorReason_UserPasswordIncorrect.String(), fmt.Sprintf(format, args...))
}func ErrorUserBanned(format string, args ...interface{}) errors.Error {return errors.New(int32(ErrorReason_UserBanned), 200, ErrorReason_UserBanned.String(), fmt.Sprintf(format, args...))
}
然后我们就可以调用这些函数来生成具体的响应码,减少我们的代码工作量
1.3 使用wire来管理依赖注入
wire是谷歌开源的一款依赖注入工具,相比于其它的反射式的依赖注入方式,wire采用代码生成的方式来完成依赖注入,代码运行效率更高
代码如下:
//go:generate wire
//go:build wireinject
// +build wireinjectpackage mainimport ("github.com/google/wire""mangokit_test/internal/conf""mangokit_test/internal/dao""mangokit_test/internal/server""mangokit_test/internal/service""github.com/mangohow/mangokit/transport/httpwrapper""github.com/sirupsen/logrus"
)func newApp(serverConf *conf.Server, dataConf *conf.Data, logger *logrus.Logger) (*httpwrapper.Server, func(), error) {panic(wire.Build(dao.ProviderSet, service.ProviderSet, server.NewHttpServer))
}
根据上面的代码,wire即可自动生成依赖创建的代码:
// Code generated by Wire. DO NOT EDIT.//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinjectpackage mainimport ("mangokit_test/internal/conf""mangokit_test/internal/dao""mangokit_test/internal/server""mangokit_test/internal/service""github.com/mangohow/mangokit/transport/httpwrapper""github.com/sirupsen/logrus"
)// Injectors from wire.go:func newApp(serverConf *conf.Server, dataConf *conf.Data, logger *logrus.Logger) (*httpwrapper.Server, func(), error) {db, cleanup, err := dao.NewFakeMysqlClient(dataConf)if err != nil {return nil, nil, err}greeterDao := dao.NewGreeterDao(db)greeterService := service.NewGreeterService(greeterDao, logger)httpwrapperServer := server.NewHttpServer(serverConf, logger, greeterService)return httpwrapperServer, func() {cleanup()}, nil
}
同样的mangokit中也添加了相应的指令来生成wire依赖注入代码
mangokit generate wire
2、mangokit实现
mangokit主要包含三个组件:
protoc-gen-go-gin
protoc-gen-go-error
mangokit
protoc-gen-go-gin
用于根据proto文件中定义的service来生成gin框架的路由代码
protoc-gen-go-error
用于根据proto文件中定义的enum来生成相应的响应错误码
mangokit中则设置了多种指令用于管理项目,比如:
- 使用create命令来生成一个初始项目结构
- 使用add命令来添加proto文件、makefile或Dockerfile
- 使用generate命令来根据proto文件生成go代码、生成openapi以及生成wire依赖注入
2.1 protobuf插件开发
在使用protoc时可以指定其它的插件用于生成代码,比如:
--go_out
则会调用protoc-gen-go插件来生成go的代码--go-grpc_out
则会调用protoc-gen-go-grpc插件来生成grpc的代码
同样的,我们可以使用go来实现一个类似的插件,从而根据proto文件来生成gin框架的代码以及响应码代码
工作原理:
在使用 protoc --go-gin_out
时,protoc
会解析proto
文件,然后生成抽象语法树
,并且它会使用protobuf
将语法树
序列化为二进制序列
,然后使用标准输入将二进制序列传入我们的插件中,然后再使用protobuf
进行反序列化
,然后我们在自己的程序中就可以根据提供的信息来生成go代码,比如:proto
中定义的message、service、enum
等
开发proto插件我们可以使用google.golang.org/protobuf/compiler/protogen
库
我们可以参考kratos的代码来实现自己的代码:
https://github.com/go-kratos/kratos/tree/main/cmd/protoc-gen-go-errors
首先看main函数:
protogen.Options.Run来运行我们的程序
在传入的匿名函数中,我们会接收到protogen.Plugin参数,该参数中有proto文件中定义的各种结构的详细信息
然后我们可以遍历每个proto文件来生成相应的代码,在generateFile中生成代码
package mainimport ("flag""fmt""google.golang.org/protobuf/compiler/protogen""google.golang.org/protobuf/types/pluginpb"
)var (showVersion = flag.Bool("version", false, "print the version and exit")
)func main() {flag.Parse()if *showVersion {fmt.Printf("protoc-gen-go-gin %v\n", version)return}protogen.Options{ParamFunc: flag.CommandLine.Set,}.Run(func(plugin *protogen.Plugin) error {plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)for _, f := range plugin.Files {if !f.Generate {continue}generateFile(plugin, f)}return nil})
}
在protogen.File中保存了一个proto文件中定义的各种结构解析后的信息:
详细代码参考:https://github.com/mangohow/mangokit
代码编写好之后编译为二进制程序,在使用protoc时指定插件名称,我们的插件一定要以protoc-gen
开头,在指定插件名称时指定protoc-gen
后面的部分拼接上_out
即可;比如protoc-gen-go-error
,在使用时为:protoc --go-error_out=. hello.proto
2.2 mangokit工具
mangokit使用cobra命令行工具开发,包含以下功能:
- 创建基础项目:根据预设的项目目录结构和代码生成
- 添加文件:包括api proto、error proto、makefile和Dockerfile
- 生成代码:包括go代码生成、wire生成和openapi生成
3、使用示例
3.1 创建新项目
首先使用mangokit来创建一个项目,项目目录为mangokit-test,go mod名称为mangokit_test
mangokit create mangokit-test mangokit_test
然后执行cd mangokit-test && go mod tidy
来下载依赖
项目目录结构如下:
$ tree
.
|-- api
| |-- errors
| | `-- v1
| | |-- errors.pb.go
| | |-- errors_errors.pb.go
| | `-- proto
| | `-- errors.proto
| `-- helloworld
| `-- v1
| |-- greeter.pb.go
| |-- greeter_http_gin.pb.go
| `-- proto
| `-- greeter.proto
|-- cmd
| `-- server
| |-- main.go
| |-- wire.go
| `-- wire_gen.go
|-- configs
| `-- application.yaml
|-- internal
| |-- conf
| | |-- conf.pb.go
| | `-- conf.proto
| |-- dao
| | |-- dao.go
| | |-- data.go
| | `-- userdao.go
| |-- middleware
| |-- model
| | `-- user.go
| |-- server
| | `-- http.go
| `-- service
| |-- helloservice.go
| `-- service.go
|-- pkg
|-- test
|-- third_party
|-- go.mod
|-- go.sum
|-- makefile
|-- Dockerfile
|-- openapi.yaml32 directories, 52 files
api:
api目录用来放置proto文件以及根据proto文件生成的go代码,通常将.proto文件放在proto文件夹下,而生成的代码放在它的上一级目录,这样看起来更清晰一些cmd:
cmd目录存放了wire注入代码和main文件configs:
configs目录用来放置程序的配置文件internal:
internal用来存放本项目依赖的代码,不会暴露给其它的项目,其中包括middleware(中间件)
、model(数据库结构体模型
)、dao(数据库访问对象
)、conf(配置信息代码)
、server(服务初始化代码)
、service(service的具体实现代码)
pkg:
用来存放一些共用代码test:
存放测试代码third_party:
其中包含一些使用到的proto的扩展文件
在创建项目时默认会从github拉取一个预制的项目结构,如果遇到网络问题导致无法拉取,则可以使用-r
命令来指定其它的仓库,比如使用gitee:
mangokit create -r https://gitee.com/mangohow/mangokit-template mangokit-test mangokit_test
3.2 添加新的proto文件
可以使用下面的命令来添加新的proto文件
# 添加http api
mangokit add api api/helloworld/v1/proto hello.proto
然后就会在api/helloworld/v1/proto目录下生成一个hello.proto文件
syntax = "proto3";package hello.v1;import "google/api/annotations.proto";option go_package = "api/helloworld/v1;v1";service Hello {}
使用下面的命令来添加error proto
mangokit add error api/errors/v1/proto errorReason.proto
同样的,在api/errors/v1/proto目录下生成了errorReason.proto文件
syntax = "proto3";package errorReason.v1;import "errors/errors.proto";option go_package = "api/errors/v1;v1";enum ErrorReason {option (errors.default_code) = 500;Placeholder = 0 [(errors.code) = 0];}
除了添加proto文件,还可以添加预制的makefile和Dockerfile
3.3 代码生成
根据proto生成代码
# 根据api目录下的proto文件生成go代码
mangokit generate proto api
根据wire依赖注入生成代码:
mangokit generate wire
生成openapi文档
mangokit generate openapi
生成上面所有的三个项目
mangokit generate all
相关文章:
mangokit:golang web项目管理工具,使用proto定义http路由和错误
文章目录 前言1、mangokit介绍1.1 根据proto文件生成http路由1.2 根据proto文件生成响应码1.3 使用wire来管理依赖注入 2、mangokit实现2.1 protobuf插件开发2.2 mangokit工具 3、使用示例3.1 创建新项目3.2 添加新的proto文件3.3 代码生成 前言 在使用gin框架开发web应用时&a…...
微信小程序实现一个简单的登录功能
微信小程序实现一个简单的登录功能 功能介绍login.wxmllogin.jsuserInfo.wxmluserInfo.js解析 功能介绍 微信小程序实现一个简单的登录功能。包括一个登录页面和一个用户信息展示页面。在登录页面中输入用户名和密码,点击登录按钮进行验证,如果验证成功&…...
whisper深入-语者分离
文章目录 学习目标:如何使用whisper学习内容一:whisper 转文字1.1 使用whisper.load_model()方法下载,加载1.2 使用实例对文件进行转录1.3 实战 学习内容二:语者分离(pyannote.audio)pyannote.audio是huggi…...
LuaJava操作Java的方法
最近在学习lua,然后顺便看了下luaj,可能用的人比较少,网上关于luaj的文章较少,其中在网上找到这个博主的相关文章,很详细,对于要学习luaj的小伙伴可以两篇一起查看,本文在此基础上进行扩展。 …...
oracle怎样才算开启了内存大页?
oracle怎样才算开启了内存大页? 关键核查下面三点: 1./etc/sysctl.conf vm.nr_hugepages16384这是给了32G,计划sga给30G,一般需多分配2-4G sysctl -p生效 看cat /proc/meminfo|grep Huge啥结果? 这种明显是配了…...
【halcon深度学习之那些封装好的库函数】determine_dl_model_detection_param
determine_dl_model_detection_param 目标检测的数据准备过程中的有一个库函数determine_dl_model_detection_param “determine_dl_model_detection_param” 直译为 “确定深度学习模型检测参数”。 这个过程会自动针对给定数据集估算模型的某些高级参数,强烈建议…...
跟着我学Python进阶篇:01.试用Python完成一些简单问题
往期文章 跟着我学Python基础篇:01.初露端倪 跟着我学Python基础篇:02.数字与字符串编程 跟着我学Python基础篇:03.选择结构 跟着我学Python基础篇:04.循环 跟着我学Python基础篇:05.函数 跟着我学Python基础篇&#…...
neo4j-Py2neo使用
neo4j-Py2neo(一):基本库介绍使用 py2neo的文档地址:https://neo4j-contrib.github.io/py2neo/ py2neo的本质是可以采用两种方式进行操作,一种是利用cypher语句,一种是使用库提供的DataTypes,Data类的实例需要和远程…...
uint29传输格式
前言 不知道谁想出来的。 反正我是想不到。 我看网上也没人讲这个。 写篇博客帮一下素未谋面的网友。 uint29 本质上是网络传输的时候,借用至多4字节Bytes,表达29位的无符号整数。 读8位数字,判断小于128? 是的话,返回末7位…...
Linux:终端定时自动注销
这样防止了,当我们临时离开电脑这个空隙,被坏蛋给趁虚而入 定几十秒或者分钟,如果这个时间段没有输入东西那么就会自动退出 全局生效 这个系统中的所有用户生效 vim /etc/profile在末尾加入TMOUT10 TMOUT10 这个就是10 秒,按…...
STM32F103RCT6开发板M3单片机教程06--定时器中断
前言 除非特别说明,本章节描述的模块应用于整个STM32F103xx微控制器系列,因为我们使用是STM32F103RCT6开发板是mini最小系统板。本教程使用是(光明谷SUN_STM32mini开发板) STM32F10X定时器(Timer)基础 首先了解一下是STM32F10X…...
数据库故障Waiting for table metadata lock
场景:早上来发现一个程序,链接mysql数据库有点问题,随后排查,因为容器在k8s里面。所以尝试重启了pod没有效果 一、重启pod: 这里是几种在Kubernetes中重启Pod的方法: 删除Pod,利用Deployment重建 kubectl delete pod mypodDepl…...
Springboot数据校验与异常篇
一、异常处理 1.1Http状态码 HTTP状态码是指在HTTP通信过程中,服务器向客户端返回的响应状态。它通过3位数字构成,第一个数字定义了响应的类别,后两位数字没有具体分类作用。以下是常见的HTTP状态码及其含义: - 1xx(信…...
第三十六章 XML 模式的高级选项 - 创建子类型的替换组
文章目录 第三十六章 XML 模式的高级选项 - 创建子类型的替换组创建子类型的替换组将子类限制在替换组中 第三十六章 XML 模式的高级选项 - 创建子类型的替换组 创建子类型的替换组 XML 模式规范还允许定义替换组,这可以是创建选择的替代方法。语法有些不同。无需…...
堆与二叉树(上)
本篇主要讲的是一些概念,推论和堆的实现(核心在堆的实现这一块) 涉及到的一些结论,证明放到最后,可以选择跳过,知识点过多,当复习一用差不多,如果是刚学这一块的,建议打…...
HBase查询的一些限制与解决方案
Apache HBase 是一个开源的、非关系型、分布式数据库,它是 Hadoop 生态系统的一部分,用于存储和处理大量的稀疏数据。HBase 在设计上是为了提供快速的随机读写能力,但与此同时,它也带来了一些查询上的限制: 没有SQL支持…...
软件开发 VS Web开发
我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版,欢迎购买。点击进入详情 目录 介绍: 角色和职责: 软件开发人员: Web开发人员: 技能: 软件开发人员: Web开发人…...
基于Springboot的旅游网站设计与实现(论文+调试+源码)
项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问…...
【从零开始学习--设计模式--策略模式】
返回首页 前言 感谢各位同学的关注与支持,我会一直更新此专题,竭尽所能整理出更为详细的内容分享给大家,但碍于时间及精力有限,代码分享较少,后续会把所有代码示例整理到github,敬请期待。 此章节介绍策…...
条款6:若不想使用编译器自动生成的函数,就该明确拒绝
有些场景我们不需要编译器默认实现的构造函数,拷贝构造函数,赋值函数,这时候我们应该明确的告诉编译器,我们不需要,一个可行的方法是将拷贝构造函数和赋值函数声明为private。 class HomeForSale { ... }; HomeForSal…...
零基础也能制作家装预约咨询小程序
近年来,随着互联网的快速发展,越来越多的消费者倾向于使用手机进行购物和咨询。然而,许多家装实体店却发现自己的客流量越来越少,急需一种新的方式来吸引顾客。而开发家装预约咨询小程序则成为了一种利用互联网技术来解决这一问题…...
Mybatis的插件运⾏原理,如何编写⼀个插件?
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🌺 仓库主页: Gitee 💫 Github 💫 GitCode 💖 欢迎点赞…...
C++复合数据类型:字符数组|读取键盘输入|简单读写文件
文章目录 字符数组(C风格字符串)读取键盘输入使用输入操作符读取单词读取一行信息getline使用get读取一个字符 读写文件 字符数组(C风格字符串) 字符串就是一串字符的集合,本质上其实是一个“字符的数组”。 在C中为了…...
Windows11环境下配置深度学习环境(Pytorch)
目录 1. 下载安装Miniconda2. 新建Python3.9虚拟环境3. 下载英伟达驱动4. 安装CUDA版Pytorch5. CPU版本pytorch安装6. 下载并配置Pycharm 1. 下载安装Miniconda 下载安装包:镜像文件地址 将Miniconda相关路径添加至系统变量的路径中。 打开Anaconda Powershell Pr…...
泛型深入理解
泛型的概述 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。 泛型的格式:<数据类型>; 注意:泛型只能支持引用数据类型。 集合体系的全部接口和实现类都是支持泛型的使用的。 泛型的…...
Linux内核模块
文章目录 一、内核模块介绍二、模块讲解1、最简模块代码:2、模块三要素3、常用操作命令3.1、 lsmod:显示已加载模块状态3.2、 insmod:载入模块3.3、rmmod:卸载模块3.4、dmesg:显示信息3.5、modinfo:显示ker…...
Java 栈和队列的交互实现
文章目录 队列和栈的区别一.用队列模拟实现栈1.1入栈1.2出栈1.3返回栈顶元素1.4判断栈是否为空 二.用栈模拟实现队列2.1 入队2.2出队2.3peek2.4判断队列是否为空 三.完整代码3.1 队列模拟实现栈3.2栈模拟实现队列 队列和栈的区别 栈和队列都是常用的数据结构,它们的…...
HarmonyOS应用开发者高级认证满分指南
声明:由于HarmonyOS应用开发者高级认证的题库一直在变,所以文章中的题目直做参考。 1. 判断题 云函数打包完成后,需要到APPGallery Connect创建对应函数的触发器才可以在端侧中调用。 【错】每一个自定义组件都有自己的生命周期。 【对】基…...
CSharp中Blazor初体验
Blazor 是一个由微软开发的开源 Web 框架,用于构建富客户端 Web 应用程序使用 C# 语言和 .NET 平台。Blazor 允许开发人员使用 C# 语言来编写前端 Web 应用程序,而不需要像传统的 JavaScript 框架(如 Angular、React 或 Vue.js)那…...
Linux下新建用户,并进行授权
注意:以下操作需要在root用户下! 新增用户 adduser 用户名设置密码 passwd 用户名更改目录所有者命令 chown -R 用户名:用户名 目录更改目录权限命令 chmod -R 755 目录...
济南能源建设网站/台州网站建设
今天无聊看了一下这个复选框的用法,用它来制作选择题的确是不错的选择。分别在组件面板拖动四个组件, 修改它的标签,让它成为自己适合的答案。还有一些data 的值,和选中状态等。 设置完组件的属性后,开始我们的制作&am…...
wordpress 搜索结果分类/谷歌sem推广
大数据是什么?在维克托迈尔-舍恩伯格及肯尼斯库克耶编写的《大数据时代》中提出:大数据指不用随机分析法(抽样调查)这样捷径,而采用所有数据进行分析处理。那么究竟多大的数据算是大数据,这个其实并没有明确…...
婚庆类网站模板/深圳网络推广网络
普通流(Normal Flow) 在普通流中,元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行,块级元素则会被渲染为完整的一个新行, 除非另外指定,否则…...
建网站的公司哪里有/百度推广关键词
我正在使用Python绑定到通过以下instructions安装的OpenCV 2.4。我的问题类似于this one,但是我需要Windows计算机解决方案。问题:当我尝试使用时cap cv2.VideoCapture(0)print cap.grab()它工作正常,但是以下代码cap cv2.VideoCapture(filename)print…...
我要找人做网站的主页/手机百度网盘网页版登录入口
目录(TOC)是Word文档的重要组成部分。它提供了文档内容的概述,并允许您快速导航到所需的部分。您可能会遇到需要以编程方式从Word文档中添加,提取,更新或删除目录的情况。为此,本文将教您如何使用C 处理Wor…...
九歌人工智能诗歌写作网站/网络推广渠道和方式
看到一篇对单例描述很不错的文章,地址:https://www.2cto.com/kf/201704/631396.html 单例设计模式总结 单例模式简介 单例模式是应用最广泛的模式之一,在应用这个模式时,单例对象的类必须确保只有一个实例存在,避免产…...