本文章所有操作基于的操作系统版本是:ubuntu16.04 64位
本文章基于的Fabric网络环境是《Fabric实战(2)运行一个简单的fabric网络(容器外)》

1 golang版本的chaincode的代码

1.1 chaincode源代码结构

下面看一最简单的chaincode代码以及相关的解释:

//包名
//一个chaincode通常是一个golang源码文件,这个包名必须是main
package main//导入包
//chaincode需要引入一些Fabric提供的系统包,这些系统包提供了chaincode和Fabirc进行通信的接口。
import ("fmt""strconv""github.com/hyperledger/fabric/core/chaincode/shim"pb "github.com/hyperledger/fabric/protos/peer"
)//定义chaincode主结构体
//每个chaincode都需要定义个结构体,结构体的名字可以是任意符合golang命名规范的字符串。chaincode的住结构体必须实现Chaincode接口
//   type Chaincode interface {//          Init(stub ChaincodeStubInterface) pb.Response
//          Invoke(stub ChaincodeStubInterface) pb.Response
//   }
type SimpleChaincode struct {}//Init方法
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {return shim.Success(nil)
}func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {function, args := stub.GetFunctionAndParameters()if function == "invoke" {return t.invoke(stub, args)}return shim.Error("Invalid invoke function name. Expecting \"invoke\"")
}func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {return shim.Success(nil)
}//main函数
//调用shim包的Start方法,启动chaincode,如果启动成功,这个函数会一直阻塞在这个地方,不会退出。
func main() {err := shim.Start(new(SimpleChaincode))if err != nil {fmt.Printf("Error starting Simple chaincode: %s", err)}
}

1.1.1 Init方法

Init方法是chaincode的初始化方法,当执行peer chaincode instantiate实例化chaincode的时候会调用该方法,同时-c 参数后面内容会作为参数传入Init方法中,以下面的实例化命令为例:

peer chaincode instantiate -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["init", "a", "100", "b", "200"]}'  -P "OR ('Org1MSP.member', 'Org2MSP.member')"

上面的命令给chaincode传入4个参数"a", “100”, “b” ,“200”。注意命令中Args后面一共有五个参数,其中第一个参数init是固定值,后面才是参数。在Init函数中可以通过下面的方法获取参数:

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {//获取客户端传入的参数,args是一个字符串切片,存储传入的字符串参数_, args := stub.GetFunctionAndParameters()return shim.Success(nil)
}

1.1.2 Invoke方法

Invoke方法的主要作用是写入数据。在执行命令peer chaincode invoke的时候会调用这个方法,同时会把命令中-c后面的参数传入Invoke方法中。以下面的invoke命令为例:

peer chaincode invoke -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["invoke", "a", "b", "1"]}'

上面的命令调用chaincode的Invoke方法并传入三个参数, 注意Args后面数组中的第一个值"invoke"是默认的固定参数。

1.2 shim包的方法

shim包常用方法

Success                     #向客户端返回正确消息
Error                       #向客户端返回错误消息
Start                       #启动chaincode
LogLevel                    #将字符串类型转化成LoggingLevel类型
SetLoggingLevel             #设置链码shim包运行日志等级
IsEnabledForLogLevel        #检查合约的是否使能制定的等级

1.2.1 Success方法

Success方法负责将正确的消息返回给调用chaincode的客户端。

//定义
func Success(payload []byte) pb.Response //示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {return shim.Success([]byte("sucess invoke"))
}

1.2.2 Error方法

Error方法负责将错误的信息返回给调用Chaincode的客户端。

// 定义
func Error(msg string) pb.Response //示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {return shim.Error([]byte("fail invoke"))
}

1.2.3 SetLoggingLevel方法

设置合约的shim包运行日志等级

// 定义
func SetLoggingLevel(level LoggingLevel) //示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {//将"debug"字符串转换成LoggingLevel类型logleve, _ := shim.LogLevel("DEBUG")//设置日志等级为debugshim.SetLoggingLevel(logleve)return shim.Success([]byte("sucess invoke"))
}

日志等级如下:

"CRITICAL"      0
"ERROR"         1
"WARNING"       2
"NOTICE"        3
"INFO"          4
"DEBUG"         5

1.2.4 IsEnabledForLogLevel方法

检查合约是否使能了日志等级

//定义
func IsEnabledForLogLevel(logLevel string) bool //示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {enabled := shim.IsEnabledForLogLevel("DEBUG")if enabled{fmt.Println("Contract enabled debug level")}return shim.Success([]byte("sucess invoke"))
}

注意:只要IsEnabledForLogLevel的参数小于当前的等级,都是返回true。比如当前的等级是DEBUG,IsEnabledForLogLevel传入的参数是ERROR,返回值也是true

1.3 shim包ChaincodeStubInterface接口

Shim包中有一个接口ChaincodeStubInterface,在Invoke方法和Query方法方法中该接口作为参数传入方法中。ChaincodeStubInterface接口提供了一组方法,通过该组方法可以非常方便的操作账本数据。ChaincodeStubInterface接口的核心方法大概可以分为四大类:

  • 系统管理
  • 存储管理
  • 交易管理
  • 调用外部chaincode

方法名列表:

GetFunctionAndParameters        #获取调用者传入的参数
GetArgs                         #获取调用者传入参数,返回二维字节切片
GetStringArgs                   #获取调用者传入参数,返回一维字符串切片
GetArgsSlice                    #获取调用者传入参数,返回字节切片
PutState                        #存储数据到账本
GetState                        #从账本中获取指定的数据
DelState                        #删除账本中的数据
GetStateByRange                 #查询指定key指定范围的数据
CreateCompositeKey              #创建复合键
GetStatePyPartialCompositeKey   #通过复合键取值
SplitCompositeKey               #拆分复合键
GetHistoryForKey                #获取制定key的历史记录
GetTxID                         #获取交易的编号
GetTxTimestamp                  #获取交易的时间
GetCreator                      #获取交易的创建者
InvokeChaincode                 #调用其他的chaincode

1.3.1 系统管理接口

1.3.1.1 GetFunctionAndParameters

该方法负责接收调用者传过来的参数。

//方法定义
GetFunctionAndParameters() (string, []string)//调用
peer chaincode instantiate -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["init", "a", "100", "b", "200"]}'  -P "OR ('Org1MSP.member', 'Org2MSP.member')"//chaincode
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {fn, args := stub.GetFunctionAndParameters()fmt.Println(fn, args)return shim.Success(nil)
}//输出
init [a 100 b 200]

1.3.1.2 GetArgs

获取调用者传入参数,返回二维字节切片

//方法定义
GetArgs() [][]byte//调用
peer chaincode instantiate -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["init", "a", "100", "b", "200"]}'  -P "OR ('Org1MSP.member', 'Org2MSP.member')"//chaincode
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {args :=  stub.GetArgs()fmt.Println(args)return shim.Success(nil)
}//输出
[[105 110 105 116] [97] [49 48 48] [98] [50 48 48]]

1.3.1.3 GetStringArgs

获取调用者传入参数,返回一维字符串切片

//方法定义
GetStringArgs() []string//调用
peer chaincode instantiate -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["init", "a", "100", "b", "200"]}'  -P "OR ('Org1MSP.member', 'Org2MSP.member')"//chaincode
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {args  := stub.GetStringArgs()fmt.Println(args)return shim.Success(nil)
}//输出
[init a 100 b 200]

1.3.1.4 GetArgsSlice

获取调用者传入参数,返回字节切片

//方法定义
GetArgsSlice() ([]byte, error)//调用
peer chaincode instantiate -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["init", "a", "100", "b", "200"]}'  -P "OR ('Org1MSP.member', 'Org2MSP.member')"//chaincode
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {args, err := stub.GetArgsSlice()if err != nil{fmt.Println(err)}fmt.Println(args )return shim.Success(nil)
}//输出
[105 110 105 116 97 49 48 48 98 50 48 48]

1.3.2 存储管理接口

1.3.2.1 PutState

该方法负责把数据保存到fabric账本中。

//方法定义
PutState(key string, value []byte) error//chaincode代码示例
type Student struct {Name  stringAge   uint8Score uint8
}func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {stu := Student{"zhangsan",25,99,}bytes, err := json.Marshal(stu)if err != nil {return shim.Error(err.Error())}stub.PutState("zhangsan", bytes)return shim.Success(nil)
}

1.3.2.2 GetState

该方法负责从fabric账本中读取数据

//方法定义
GetState(key string) ([]byte, error)//chaincode代码示例
type Student struct {Name  stringAge   uint8Score uint8
}func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {bytes, err := stub.GetState("zhangsan")if err != nil{return  shim.Error(err.Error())}stu := new(Student)err = json.Unmarshal(bytes, stu)if err != nil{return  shim.Error(err.Error())}fmt.Println(stu)return shim.Success(nil)
}

1.3.2.3 DelState

DelState用来删除账本中的key,如果对一个不存在的的key-value进行删除不会返回错误

//方法定义
DelState(key string) error
//chaincode代码示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {err := stub.DelState("zhangsan")if err != nil{fmt.Println(err)}return shim.Success(nil)
}

1.3.2.4 GetStateByRange

根据key的范围来查询数据

//方法定义
//包含startKey,不包含endKey
//如果startKey和endKey为空,则无限范围查询
GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error)//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {stub.PutState("1", []byte("zhangsan"))stub.PutState("2", []byte("lisi"))stub.PutState("3", []byte("wangwu"))stub.PutState("4", []byte("zhaoliu"))stub.PutState("5", []byte("sunqi"))return shim.Success(nil)
}func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {keysIter, err := stub.GetStateByRange("2", "5")if err != nil{return shim.Error(err.Error())}rsp := make(map[string]string)for keysIter.HasNext(){response, interErr := keysIter.Next()if interErr != nil{return shim.Error(interErr.Error())}rsp[response.Key] = string(response.Value)fmt.Println(response.Key, string(response.Value))}//将结果以json字符串返回jsonRsp, err := json.Marshal(rsp)if err != nil{return shim.Error(err.Error())}return shim.Success(jsonRsp)
}func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {function, args := stub.GetFunctionAndParameters()if function == "set" {return t.set(stub, args)} else if function == "get" {return t.get(stub, args)}return shim.Success(nil)
}

链码日志输出:

2 lisi
3 wangwu
4 zhaoliu

1.3.2.5 GetHistoryForKey

查询某个键的历史修改记录。返回的记录包括交易的编号、修改的值、当前key的有没有被删除,交易发生的时间戳,GetHistoryForKey需要peer的配置参数core.ledger.history.enableHistoryDatabase为true。
注意:

1.查询交易所在区块中其他更新操作,本次查询无法查询到。

//方法定义
GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error)
//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {name := args[0]err := stub.PutState("userName", []byte(name))if err != nil{return shim.Error(err.Error())}return shim.Success(nil)
}func (t *SimpleChaincode) history(stub shim.ChaincodeStubInterface, args []string)pb.Response {keyInter, err := stub.GetHistoryForKey("name")if err != nil{return shim.Error(err.Error())}for keyInter.HasNext(){response, interErr := keyInter.Next()if interErr != nil{return shim.Error(interErr.Error())}txid := response.TxIdtxvalue := response.Valuetxstatus := response.IsDeletetxtimestamp := response.Timestamptm := time.Unix(txtimestamp.Seconds, 0)timeString := tm.Format("2006-01-02 03:04:05 PM")fmt.Println(txid, string(txvalue), txstatus, timeString)}return shim.Success(nil)
}

链码日志输出:
先调用链码的set方法修改三次,然后在调用history方法获取记录

peer chaincode invoke -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["set", "zhangsan"]}'peer chaincode invoke -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["set", "lisi"]}'peer chaincode invoke -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0    -c '{"Args":["set", "wangwu"]}'
bdfd3d29622d41350493d5421d6759c56a38273033a31e3d70e091856e62dc69 zhangsan false 2019-03-14 03:28:02 PM
692dba2bf1c7c24ede17248310df58726a974210afe2097b3ff10ce8a3381ab5 lisi false 2019-03-14 03:28:02 PM
5864c768fad78049969891a315838ed1336fa86ed138f2ca87aee4e09900b4bc wangwu false 2019-03-14 03:28:02 PM

1.3.2.4 CreateCompositeKey

创建组合间,用户查询

//方法定义
CreateCompositeKey(objectType string, attributes []string) (string, error)//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {stus := []Student{{"lisi", 20, 99},{"lisi",20,98},{"lisi",21,100},}for i, _ := range(stus) {stu := stus[i]key, err := stub.CreateCompositeKey(stu.Name, []string{strconv.Itoa(stu.Age), strconv.Itoa(stu.Score)})if err != nil {fmt.Println(err)return shim.Error(err.Error())}bytes, err := json.Marshal(stu)if err != nil {fmt.Println(err)return shim.Error(err.Error())}stub.PutState(key, bytes)}return shim.Success(nil)
}func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {rs, err := stub.GetStateByPartialCompositeKey("lisi", []string{})if err != nil{fmt.Println(err)return  shim.Error(err.Error())}defer rs.Close()for rs.HasNext(){responseRange, err := rs.Next()if err != nil{fmt.Println(err)}stu := new(Student)err = json.Unmarshal(responseRange.Value, stu)if err != nil{fmt.Println(err)}fmt.Println(responseRange.Key, stu)}return shim.Success(nil)
}

链码日志输出:

lisi2098 &{lisi 20 98}
lisi2099 &{lisi 20 99}
lisi21100 &{lisi 21 100}

由于stub.GetStateByPartialCompositeKey(“lisi”, []string{}),只指定了一个主键,所以把叫lisi的学生都查询出来了。

如果按修改代码如下:

 rs, err := stub.GetStateByPartialCompositeKey("lisi", []string{"20"})

链码日志输出如下:

lisi2098 &{lisi 20 98}
lisi2099 &{lisi 20 99}

由于21岁的lisi不满足组合键要求,所以没有查询出来

1.3.2.5 GetStatePyPartialCompositeKey

通过组合键进行查询, 相当于模糊查询

//方法定义
GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error)//chaincode代码示例
调用示例参考CreateCompositeKey方法的示例

1.3.2.6 SplitCompositeKey

分割组合键, 将创建的组合键分割开来

//方法定义
SplitCompositeKey(compositeKey string) (string, []string, error)
//chaincode代码示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {key, _ := stub.CreateCompositeKey("sili", []string{"20", "100"})key1, keyPart, _:= stub.SplitCompositeKey(key)fmt.Println(key1, keyPart)return shim.Success(nil)
}

链码日志输出:

sili20100
sili [20 100]

1.3.3 交易管理接口

1.3.3.1 GetTxID

获取当前调用交易的ID号

//方法定义
GetTxID() string//chaincode代码示例
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {id := stub.GetTxID()fmt.Println(id)return shim.Success(nil)
}

链码日志输出:

66a925816afb2c2067f014a29ad8609ae40a405d5997f79c4c930e4d79c2eb5f

1.3.3.2 GetCreator

获得提案的签名者证书


//方法定义
GetTxID() string//chaincode代码示例
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {id := stub.GetTxID()fmt.Println(id)return shim.Success(nil)
}

链码日志输出:

Org1MSP�-----BEGIN -----
MIICLjCCAdWgAwIBAgIQJcxoHNiOAWmIXH7VZ/6fDDAKBggqhkjOPQQDAjCBgTEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xIDAeBgNVBAoTF29yZzEuc2ltcGxlLW5ldHdvcmsuY29tMSMwIQYD
VQQDExpjYS5vcmcxLnNpbXBsZS1uZXR3b3JrLmNvbTAeFw0xOTAzMTAxNTA5MzBa
Fw0yOTAzMDcxNTA5MzBaMGIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMSYwJAYDVQQDDB1BZG1pbkBvcmcx
LnNpbXBsZS1uZXR3b3JrLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJWM
1LNOMnDGnbpe0XEx7HNFyBZgOZk5E+PDtHer/ZbbFvCIvoFoafzw7qIwlOxXTj99
bOG3kHQX2OPp/+7/nEajTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAA
MCsGA1UdIwQkMCKAIPXBTNkkfW7yIcRyZvip+yXmvzwPBujCNja1BMv3f+BmMAoG
CCqGSM49BAMCA0cAMEQCIG1Q+qvhCSv/GZtINP51WZLUpUDtABiY8RsmetPqK1Rh
AiBsYoFWSQeVRspiTuEz1dhW+ke2iq05P3k+gFzN1yiUUw==
-----END -----

1.3.3.3 GetTxTimestamp

//方法定义
GetTxTimestamp() (*timestamp.Timestamp, error)//chaincode代码示例
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {timestamp, _ := stub.GetTxTimestamp()tm:= time.Unix(timestamp.Seconds, 0)ts := tm.Format("2006-01-02 03:04:05 PM")fmt.Println(ts)return shim.Success(nil)
}

链码日志输出:

2019-03-14 06:12:49 PM

1.3.4 调用外部chaincode

1.3.4.1 InvokeChaincode

在chaincode中可以调用其他的chaincode。

//方法定义
InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {parms := []string{"get"}querArgs := make([][]byte, len(parms))for i, arg := range parms{querArgs[i]  = []byte(arg)}response := stub.InvokeChaincode("r_test_cc8", querArgs, "testchannel")if response.Status != shim.OK{err := fmt.Sprintln("fail to query chaincode, Got error :%s", response.Payload)shim.Error(err)}return shim.Success(nil)
}//r_test_cc8链码中的get函数实现
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {fmt.Println("Thank you for calling me")return shim.Success(nil)
}

r_test_cc8链码日志输出:

Thank you for calling me

Fabric实战(10)链码(chaincode)开发-shim包API相关推荐

  1. Hyperledger Fabric 实战(七):链码 shim API 详解

    为什么80%的码农都做不了架构师?>>>    用 Go 的链码的 shim API 主要方法详解 GetFunctionAndParameters 获取方法名和参数 invoke的 ...

  2. Fabric学习笔记(六) - chaincode开发模式

    启动网络 terminal1 cd /opt/gopath/src/github.com/hyperledger/fabric-samples/chaincode-docker-devmodedock ...

  3. Fabric - chaincode开发模式

    Fabric学习笔记(六) - chaincode开发模式 启动网络 terminal1 cd /opt/gopath/src/github.com/hyperledger/fabric-sample ...

  4. Hyperledger Fabric 1.0 实战开发系列 第三课 chaincode开发

    chaincode是由go语言写的,实现了定义的接口.其他语言例如JAVA也是支持的.通过application体积的transaction,chaincode可以初始化并管理Ledger状态. 一个 ...

  5. Hyperledger Fabric Chaincode 开发

    好了,进入正题.我今天分享的内容的题目是Fabric1.0 Chaincode介绍.除了介绍Chaincode程序编写.调试的基本方法之外,我还加入了一些有关Chaincode原理的内容,希望能够帮助 ...

  6. Hyperledger Fabric chaincode 开发(疑难解答)

    Q&A Q1: 使用fabric release 1.2 进行golang chaincode开发时报错: ..\..\hyperledger\fabric\vendor\github.com ...

  7. hyperledger fabric 实战开发——水产品溯源交易平台(二)

    文章目录 前言 一.技术学习 1.Hyperledger fabric 1.1 流程 1.2 配置 1.3 范例解析并自写 1.3 算法实现 二.Web编写 前言 hyperledger fabric ...

  8. Fabric 链码Chaincode 的安装、初始化、调用、升级

    Fabric 链码Chaincode 的安装.初始化.调用.升级 Fabric chaincode 上一篇文章,我们启动了一个Fabric网络,这篇文章来看看在Fabric网络进行应用的开发. 上一篇 ...

  9. Fabric chaincode开发调试

    Fabric chaincode开发调试 由于chaincode开发调试步骤稍多,每次都要查看官方doc有些不便,且偶尔还会遇到官方doc无法访问的情况,故整理一份chaincode开发步骤(环境已经 ...

  10. Fabric系列 - 链码 ChainCode

    链码(chaincode)是 Hyperledger Fabric 提供的智能合约,是上层应用与底层区块链平台交互的媒介.现阶段,Fabric 提供 Go.Java.Node.js 等语言编写的链码 ...

最新文章

  1. nodejs 开发企业微信第三方应用入门教程
  2. java 复杂报表_Java+POI+模板”一:打造复杂Excel 报表
  3. 北大青鸟广州天河:高中生做技术经理!
  4. 原始尺寸_螺母尺寸检测,螺丝螺母外观检测设备
  5. win8.1 计算机放在桌面,Win8.1怎么把开始屏幕中的程序放到桌面?
  6. SAP Spartacus UsersSelectors.getAddressesLoading
  7. 使用.NET 5自动查找代码中的潜在错误
  8. 都 2021 了,你还忘记关闭 http body?
  9. 关于3Q大战和反垄断
  10. 光标移动事件。 gridview光标移动变色
  11. R语言生存分析之竞争风险模型
  12. android手机锁屏密码忘记,安卓手机锁屏密码忘了怎么办 解决锁屏密码六种方法介绍...
  13. 徐思201771010132《面向对象程序设计(java)》第十六周学习总结
  14. 第二人生的源码分析(三十九)关闭WinXP的错误报告功能
  15. Linux系统 运行小花仙游戏(针对2021年Flash停止维护的情况)
  16. 最佳二次逼近多项式MATLAB代码,数学实验“Chebshev多项式最佳一致逼近,最佳平方逼近”实验报告(内含matlab程序).doc...
  17. 重建古老计算机Pong
  18. python计算平均分_自动计算平均学分绩点的Python实现
  19. 使用C#实现邮箱验证
  20. JavaScript之BOM

热门文章

  1. 新连接、新生意、新生态,专访快手商业生态开放平台
  2. iOS之安装包优化以及瘦身
  3. matlab dwt函数应用,MATLAB中关于DCT,DFT和DWT的相关函数
  4. 生成图片带有随机码的验证码
  5. 【为什么我在namesilo买的域名说我没有备案 】求大佬解答
  6. python 求和_python pandas行、列求和及累加求和
  7. 万年历节日c语言,万年历C语言
  8. DNS域名解析错误解决
  9. oracle 包头 和 包体,PL/SQL 包头和包体
  10. 逸鹏说道:性格色彩读后感