├── chat_robot ├── app │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-37.pyc │ └── templates │ │ └── index.html ├── requirements.txt ├── hello.py ├── README.md └── chat_server.py ├── flask_api ├── tests │ ├── file.txt │ ├── test_parametrize.py │ ├── api_test_AES.py │ ├── api_test_sign.py │ └── api_test.py ├── uploads │ ├── file.txt │ └── log.txt ├── .flaskenv ├── requirements.txt ├── README.md └── api_server.py ├── graphql_demo ├── __init__.py ├── requirements.txt ├── graphql.png ├── app.py ├── README.md └── schema.py ├── mock-hook ├── tests │ └── __init__.py ├── mock_hook │ ├── __init__.py │ ├── cli.py │ └── common.py ├── dist │ ├── mock_hook-0.1.1.tar.gz │ └── mock_hook-0.1.1-py3-none-any.whl ├── __pycache__ │ └── my_hook.cpython-39.pyc ├── my_response.json ├── my_request.json ├── pyproject.toml ├── README.md ├── my_hook.py └── poetry.lock ├── learnAPI ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── java │ │ │ └── learnAPI │ │ │ ├── Greeting.java │ │ │ └── LearnApiApplication.java │ └── test │ │ └── java │ │ └── com │ │ └── java │ │ └── learnAPI │ │ └── LearnApiApplicationTests.java ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.properties │ │ └── MavenWrapperDownloader.java ├── .gitignore ├── README.md ├── pom.xml ├── mvnw.cmd └── mvnw ├── fastapi_sample ├── requirements.txt ├── README.md └── main.py ├── sanic_app ├── requirements.txt ├── login.py ├── server.py └── auth.py ├── sys_monitoring ├── requirements.txt ├── README.md ├── websocket_server.py └── index.html ├── goapi ├── main.go ├── .idea │ ├── modules.xml │ └── goapi.iml ├── Controllers │ ├── common │ │ └── response.go │ └── api_server.go ├── router │ └── router.go ├── test │ └── api_server_test.go ├── proxy_api │ └── main.go ├── go.mod ├── README.md ├── mock_api │ └── main.go └── go.sum ├── README.md └── httpx ├── httpx_sync.py └── httpx_async.py /chat_robot/app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flask_api/tests/file.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flask_api/uploads/file.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flask_api/uploads/log.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /graphql_demo/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mock-hook/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mock-hook/mock_hook/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flask_api/.flaskenv: -------------------------------------------------------------------------------- 1 | FLASK_APP=api_server -------------------------------------------------------------------------------- /learnAPI/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /chat_robot/requirements.txt: -------------------------------------------------------------------------------- 1 | sanic==21.3.4 2 | jinja2==3.0.1 3 | httpx==0.23.0 -------------------------------------------------------------------------------- /fastapi_sample/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi==0.65.2 2 | python-multipart==0.0.5 3 | -------------------------------------------------------------------------------- /sanic_app/requirements.txt: -------------------------------------------------------------------------------- 1 | sanic==23.3.0 2 | sanic-ext==23.3.0 3 | PyJWT==2.6.0 4 | -------------------------------------------------------------------------------- /graphql_demo/requirements.txt: -------------------------------------------------------------------------------- 1 | flask-graphql==2.0.1 2 | graphql-server==3.0.0b3 3 | graphql-core==3.1.3 -------------------------------------------------------------------------------- /flask_api/requirements.txt: -------------------------------------------------------------------------------- 1 | flask==3.1.0 2 | flask_cors==5.0.0 3 | requests==2.25.0 4 | pycryptodome==3.21.0 -------------------------------------------------------------------------------- /graphql_demo/graphql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/learning-API-test/HEAD/graphql_demo/graphql.png -------------------------------------------------------------------------------- /sys_monitoring/requirements.txt: -------------------------------------------------------------------------------- 1 | flask==1.1.2 2 | psutil==5.8.0 3 | Flask-SocketIO==4.3.2 4 | flask-graphql==2.0.1 -------------------------------------------------------------------------------- /mock-hook/dist/mock_hook-0.1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/learning-API-test/HEAD/mock-hook/dist/mock_hook-0.1.1.tar.gz -------------------------------------------------------------------------------- /mock-hook/__pycache__/my_hook.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/learning-API-test/HEAD/mock-hook/__pycache__/my_hook.cpython-39.pyc -------------------------------------------------------------------------------- /mock-hook/dist/mock_hook-0.1.1-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/learning-API-test/HEAD/mock-hook/dist/mock_hook-0.1.1-py3-none-any.whl -------------------------------------------------------------------------------- /chat_robot/app/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/learning-API-test/HEAD/chat_robot/app/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /mock-hook/my_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "error": { 4 | "code": "", 5 | "response": "" 6 | }, 7 | "result": [] 8 | } -------------------------------------------------------------------------------- /goapi/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "goapi/router" 5 | ) 6 | 7 | func main() { 8 | r := router.InitRouter() 9 | r.Run(":8081") 10 | } 11 | -------------------------------------------------------------------------------- /mock-hook/my_request.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": { 3 | "token": "abc123", 4 | "user-email": "user@gmail.com" 5 | }, 6 | "param": { 7 | "id": "123", 8 | "name": "henry" 9 | } 10 | } -------------------------------------------------------------------------------- /learnAPI/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chat_robot/hello.py: -------------------------------------------------------------------------------- 1 | from sanic import Sanic 2 | from sanic.response import json 3 | 4 | app = Sanic("My Hello, world app") 5 | 6 | 7 | @app.route('/') 8 | async def test(request): 9 | return json({'hello': 'world'}) 10 | 11 | if __name__ == '__main__': 12 | app.run() 13 | -------------------------------------------------------------------------------- /goapi/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /flask_api/tests/test_parametrize.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def colors(): 5 | colors_list = [] 6 | for i in range(10): 7 | colors_list.append("red"+str(i)) 8 | return colors_list 9 | 10 | 11 | @pytest.mark.parametrize('color', colors()) 12 | def test_colors(color): 13 | assert color != 'mauve' 14 | -------------------------------------------------------------------------------- /goapi/Controllers/common/response.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | /* 6 | 封装接口返回 response 格式 7 | */ 8 | func Response(code string, message string, data string) gin.H { 9 | resp := gin.H{ 10 | "code": code, 11 | "message": message, 12 | "data": data, 13 | } 14 | return resp 15 | } -------------------------------------------------------------------------------- /learnAPI/src/test/java/com/java/learnAPI/LearnApiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.java.learnAPI; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class LearnApiApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sanic_app/login.py: -------------------------------------------------------------------------------- 1 | import jwt 2 | from sanic import Blueprint, text 3 | 4 | login = Blueprint("login", url_prefix="/login") 5 | 6 | 7 | @login.post("/") 8 | async def do_login(request): 9 | print("request.app.config.SECRET", request.app.config.SECRET) 10 | token = jwt.encode({}, request.app.config.SECRET) 11 | return text(token) 12 | -------------------------------------------------------------------------------- /sys_monitoring/README.md: -------------------------------------------------------------------------------- 1 | # system monitoring 2 | 3 | 监听系统资源使用 4 | 5 | 安装: 6 | ```shell script 7 | pip install flask==1.1.2 8 | pip install psutil==5.8.0 9 | pip install Flask-SocketIO==4.3.2 10 | ``` 11 | 12 | 启动: 13 | ```shell script 14 | > python websocket_server.py 15 | ``` 16 | 17 | 通过浏览器访问 18 | ``` 19 | http://127.0.0.1:9090/ 20 | ``` 21 | -------------------------------------------------------------------------------- /goapi/.idea/goapi.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mock-hook/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "mock-hook" 3 | version = "0.1.1" 4 | description = "" 5 | authors = ["defnngj "] 6 | readme = "README.md" 7 | packages = [{include = "mock_hook"}] 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.9" 11 | click = "^8.1.3" 12 | 13 | [tool.poetry.scripts] 14 | copycat = "mock_hook.cli:main" 15 | 16 | [build-system] 17 | requires = ["poetry-core"] 18 | build-backend = "poetry.core.masonry.api" 19 | -------------------------------------------------------------------------------- /learnAPI/src/main/java/com/java/learnAPI/Greeting.java: -------------------------------------------------------------------------------- 1 | package com.java.learnAPI; 2 | 3 | public class Greeting { 4 | private final long id; 5 | private final String content; 6 | 7 | public Greeting(long id, String content) { 8 | this.id = id; 9 | this.content = content; 10 | } 11 | 12 | public long getId() { 13 | return id; 14 | } 15 | public String getContent(){ 16 | return content; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /goapi/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "goapi/Controllers" 6 | ) 7 | 8 | func InitRouter() *gin.Engine { 9 | router := gin.Default() 10 | 11 | apiV1 := router.Group("api/v1") 12 | { 13 | 14 | apiV1.GET("/", Controllers.Hello) 15 | apiV1.GET("/user/:name", Controllers.HelloName) 16 | apiV1.GET("/users", Controllers.UserInfo) 17 | apiV1.POST("/login", Controllers.Login) 18 | } 19 | return router 20 | 21 | } 22 | -------------------------------------------------------------------------------- /graphql_demo/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | from flask_graphql import GraphQLView 4 | from schema import Schema 5 | 6 | app = Flask(__name__) 7 | 8 | 9 | def create_app(path="/graphql", **kwargs): 10 | app.add_url_rule( 11 | path, view_func=GraphQLView.as_view("graphql", schema=Schema, **kwargs) 12 | ) 13 | return app 14 | 15 | 16 | if __name__ == "__main__": 17 | app = create_app(graphiql=True) 18 | app.debug = True 19 | app.run() 20 | -------------------------------------------------------------------------------- /fastapi_sample/README.md: -------------------------------------------------------------------------------- 1 | # fastapi 2 | 3 | * 介绍: 4 | fastapi是最近非常火的python Web框架,通过名字可以看出,它是专注于后端API接口开发的框架。 5 | 6 | * 文档: 7 | https://fastapi.tiangolo.com/zh/ 8 | 9 | * 运行: 10 | ```shell 11 | > uvicorn main:app --reload 12 | INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) 13 | INFO: Started reloader process [17208] using statreload 14 | INFO: Started server process [22252] 15 | INFO: Waiting for application startup. 16 | INFO: Application startup complete. 17 | ``` -------------------------------------------------------------------------------- /sanic_app/server.py: -------------------------------------------------------------------------------- 1 | from sanic import Sanic 2 | from sanic.response import json 3 | 4 | from auth import protected 5 | from login import login 6 | 7 | 8 | app = Sanic("AuthApp") 9 | app.config.SECRET = "KEEP_IT_SECRET_KEEP_IT_SAFE" 10 | app.blueprint(login) 11 | 12 | 13 | @app.get("/") 14 | async def hello_world(request): 15 | return json({"msg": "Hello, world."}) 16 | 17 | 18 | @app.get("/secret") 19 | @protected 20 | async def secret(request): 21 | return json({"msg": "To go fast, you must be fast."}) 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # learning-API-test 2 | 3 | 学习API开发与测试,包含一些有意思的项目。 4 | 5 | | 项目 | 说明 | 6 | | ---- | ---- | 7 | | [chat_robot](./chat_robot) | 基于sanic框架实现聊天机器人,主要学习websocket长连接 | 8 | | [flask_api](./flask_api) | 基于flask框架,学习HTTP接口测试,里面提供了大量的例子和测试用例 | 9 | | [goapi](./goapi) | 基于gin框架实现API, 学习go web开发| 10 | | [graphql_demo](./graphql_demo) | graphQL是API的查询语言,这里基于flask实现graphQL API | 11 | | [httpx](./httpx) | httpx是下一代HTTP库,支持异步,支持HTTP/2,用法与requests类似 | 12 | | [sys_monitoring](./sys_monitoring) | 基于flask socketio实现一个监控系统,监控本机的CPU/内存 | 13 | -------------------------------------------------------------------------------- /goapi/test/api_server_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "goapi/router" 10 | ) 11 | 12 | func TestHelloRoute(t *testing.T) { 13 | 14 | router := router.InitRouter() 15 | w := httptest.NewRecorder() 16 | 17 | req, _ := http.NewRequest("GET", "/api/v1/", nil) 18 | router.ServeHTTP(w, req) 19 | 20 | assert.Equal(t, 200, w.Code) 21 | assert.Equal(t, "{\"code\":\"10200\",\"data\":\"hello gin!\",\"message\":\"success\"}", w.Body.String()) 22 | } 23 | -------------------------------------------------------------------------------- /learnAPI/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /httpx/httpx_sync.py: -------------------------------------------------------------------------------- 1 | # 同步调用 2 | import time 3 | import httpx 4 | 5 | 6 | def make_request(client): 7 | resp = client.get('http://192.168.0.7:5000') 8 | result = resp.json() 9 | # print(result) 10 | assert result["code"] == 10200 11 | 12 | def main(): 13 | session = httpx.Client() 14 | 15 | # 1000 次调用 16 | for _ in range(1000): 17 | make_request(session) 18 | 19 | 20 | if __name__ == '__main__': 21 | # 开始 22 | start = time.time() 23 | main() 24 | # 结束 25 | end = time.time() 26 | print(f'同步:发送1000次请求,耗时:{end - start}') 27 | -------------------------------------------------------------------------------- /graphql_demo/README.md: -------------------------------------------------------------------------------- 1 | # Flask-Graphql 使用 demo 2 | 3 | ## 安装 4 | 5 | 克隆或下载项目,安装依赖。 6 | 7 | ```shell 8 | $ pip install -r requirements.txt 9 | ``` 10 | 11 | ## 启动服务 12 | 13 | ```shell 14 | $ python app.py 15 | 16 | * Serving Flask app "app.py" (lazy loading) 17 | * Environment: production 18 | WARNING: Do not use the development server in a production environment. 19 | Use a production WSGI server instead. 20 | * Debug mode: on 21 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 22 | * Restarting with stat 23 | * Debugger is active! 24 | * Debugger PIN: 208-740-173 25 | ``` 26 | 27 | ## Graphql 使用 28 | 29 | 访问:`http://127.0.0.1:5000/graphql` 30 | 31 |  32 | -------------------------------------------------------------------------------- /mock-hook/README.md: -------------------------------------------------------------------------------- 1 | # mock-hook 2 | 3 | 4 | ## install 5 | 6 | * 打包 7 | 8 | ```bash 9 | > python3.9 -m poetry build 10 | ``` 11 | 12 | * 安装 13 | 14 | ```bash 15 | > python3.9 -m pip install ./dist/mock_hook-0.1.1-py3-none-any.whl 16 | ``` 17 | 18 | * 卸载 19 | 20 | ```bash 21 | > python3.9 -m pip uninstall mock_hook 22 | ``` 23 | 24 | ## Used 25 | 26 | * `copycat` cli tools. 27 | 28 | ``` 29 | > copycat --help 30 | Usage: copycat [OPTIONS] 31 | 32 | Copycat CLI. 33 | 34 | Options: 35 | -h, --hook TEXT hook function file. 36 | -q, --request TEXT request data file. 37 | -s, --response TEXT response data file. 38 | --help Show this message and exit. 39 | ``` 40 | -------------------------------------------------------------------------------- /mock-hook/mock_hook/cli.py: -------------------------------------------------------------------------------- 1 | import click 2 | from .common import loader, read_data 3 | 4 | 5 | @click.command() 6 | @click.option('-h', '--hook', help='hook function file.') 7 | @click.option('-q', '--request', help='request data file.') 8 | @click.option('-s', '--response', help='response data file.') 9 | def main(hook, request, response): 10 | """Copycat CLI.""" 11 | 12 | if hook: 13 | req_data = read_data(request) 14 | resp_data = read_data(response) 15 | # check hook function 16 | resp = loader(file_name=hook, request=req_data, response=resp_data) 17 | click.echo(f"response:\n {resp}") 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /chat_robot/README.md: -------------------------------------------------------------------------------- 1 | # 智能聊天 web 版 2 | 3 | 4 | sanic官网: 5 | 6 | https://sanicframework.org/ 7 | 8 | ## 安装 9 | 10 | ```shell 11 | pip3 install sanic==21.3.4 12 | pip3 install jinja2==3.0.1 13 | ``` 14 | 15 | ## 运行 16 | 17 | ```shell 18 | > python chat_server.py 19 | [2021-05-28 16:17:16 +0800] [20816] [INFO] Goin' Fast @ http://127.0.0.1:8000 20 | [2021-05-28 16:17:16 +0800] [20816] [INFO] Starting worker [20816] 21 | ``` 22 | 23 | 或者 24 | 25 | ```shell 26 | > sanic chat_server.app 27 | [2021-05-28 16:16:29 +0800] [19556] [INFO] Goin' Fast @ http://127.0.0.1:8000 28 | [2021-05-28 16:16:29 +0800] [19556] [INFO] Starting worker [19556] 29 | ``` 30 | 31 | 32 | 33 | ## 使用 34 | 35 | 通过浏览器访问:http://127.0.0.1:8000 36 | 37 | -------------------------------------------------------------------------------- /mock-hook/my_hook.py: -------------------------------------------------------------------------------- 1 | from seldom.testdata import get_date 2 | 3 | def response_hook(request, response): 4 | print("hook request", request) 5 | print("hook response", response) 6 | 7 | 8 | # 判断请求头 token 为空,返回失败 9 | token = request["header"]["token"] 10 | if token == "": 11 | response["error"]["code"] = "100501" 12 | response["error"]["message"] = "token is null" 13 | return response 14 | 15 | # 自定义数据,插入到reponse中 16 | response["result"] = {"total": 100, "project_list": []} 17 | 18 | # 把 header请求头的数据,插入到reponse中 19 | response["result"]["token"] = token 20 | 21 | # 把 随机生成 的数据,插入到 reponse中 22 | response["result"]["today"] = get_date() 23 | 24 | return response 25 | 26 | -------------------------------------------------------------------------------- /httpx/httpx_async.py: -------------------------------------------------------------------------------- 1 | # 异步调用 2 | import httpx 3 | import asyncio 4 | import time 5 | 6 | async def request(client): 7 | resp = await client.get('http://192.168.0.7:5000') 8 | result = resp.json() 9 | # print(result) 10 | assert result["code"] == 10200 11 | 12 | 13 | async def main(): 14 | async with httpx.AsyncClient() as client: 15 | # # 开始 16 | # start = time.time() 17 | 18 | # 1000 次调用 19 | task_list = [] 20 | for _ in range(1000): 21 | req = request(client) 22 | task = asyncio.create_task(req) 23 | task_list.append(task) 24 | await asyncio.gather(*task_list) 25 | 26 | 27 | if __name__ == "__main__": 28 | #开始 29 | start = time.time() 30 | asyncio.run(main()) 31 | # 结束 32 | end = time.time() 33 | print(f'异步:发送1000次请求,耗时:{end - start}') -------------------------------------------------------------------------------- /sanic_app/auth.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | import jwt 4 | from sanic import text 5 | 6 | 7 | def check_token(request): 8 | if not request.token: 9 | return False 10 | 11 | try: 12 | jwt.decode( 13 | request.token, request.app.config.SECRET, algorithms=["HS256"] 14 | ) 15 | except jwt.exceptions.InvalidTokenError: 16 | return False 17 | else: 18 | return True 19 | 20 | 21 | def protected(wrapped): 22 | def decorator(f): 23 | @wraps(f) 24 | async def decorated_function(request, *args, **kwargs): 25 | is_authenticated = check_token(request) 26 | 27 | if is_authenticated: 28 | response = await f(request, *args, **kwargs) 29 | return response 30 | else: 31 | return text("You are unauthorized.", 401) 32 | 33 | return decorated_function 34 | 35 | return decorator(wrapped) 36 | -------------------------------------------------------------------------------- /learnAPI/README.md: -------------------------------------------------------------------------------- 1 | # java spring 2 | 3 | ## 安装环境 4 | 5 | 1. java JDK 6 | 7 | ```shell 8 | ❯ java -version 9 | java version "1.8.0_251" 10 | Java(TM) SE Runtime Environment (build 1.8.0_251-b08) 11 | Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode) 12 | ``` 13 | 14 | 2. maven 15 | 16 | ```shell 17 | ❯ mvn -version 18 | D:\ 19 | Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) 20 | Maven home: D:\Java\apache-maven-3.6.3\bin\.. 21 | Java version: 1.8.0_251, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk1.8.0_251\jre 22 | Default locale: zh_CN, platform encoding: GBK 23 | OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows" 24 | ``` 25 | 26 | 3. spring 2.4.5 27 | 28 | https://start.spring.io/ 29 | 30 | 4. idea 2021.1 31 | 32 | https://www.jetbrains.com/idea/download/ 33 | 34 | ## 运行项目 35 | 36 | 1. 安装依赖 37 | 38 | ``` 39 | > mvn install 40 | ``` 41 | 42 | 2. 运行项目 43 | ``` 44 | > mvn spring-boot:run 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /learnAPI/src/main/java/com/java/learnAPI/LearnApiApplication.java: -------------------------------------------------------------------------------- 1 | package com.java.learnAPI; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import java.util.concurrent.atomic.AtomicLong; 10 | 11 | 12 | @SpringBootApplication 13 | @RestController 14 | public class LearnApiApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(LearnApiApplication.class, args); 18 | } 19 | 20 | @GetMapping("/hello") 21 | public String hello(@RequestParam(value = "name", defaultValue = "World") String name) { 22 | return String.format("Hello %s!", name); 23 | } 24 | 25 | private static final String template = "Hello, %s!"; 26 | private final AtomicLong counter = new AtomicLong(); 27 | 28 | @GetMapping("/greeting") 29 | public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) { 30 | return new Greeting(counter.incrementAndGet(), String.format(template, name)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /goapi/proxy_api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httputil" 7 | "net/url" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func proxy(c *gin.Context) { 13 | proxyPath := c.Param("proxyPath") 14 | fmt.Printf("proxyPath: %+v\n", proxyPath) 15 | 16 | remote, err := url.Parse("https://httpbin.org") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | proxy := httputil.NewSingleHostReverseProxy(remote) 22 | 23 | mockMode := c.Request.Header.Get("mock-mode") 24 | fmt.Println("headers: mock-mode=", mockMode) 25 | if mockMode == "1" { 26 | // 代理到指定的服务 27 | // 设置 Header 请求头 28 | c.Request.Header.Set("cookie", "xxxxx") 29 | proxy.Director = func(req *http.Request) { 30 | req.Header = c.Request.Header 31 | req.Host = remote.Host 32 | req.URL.Scheme = remote.Scheme 33 | req.URL.Host = remote.Host 34 | req.URL.Path = c.Param("proxyPath") 35 | } 36 | 37 | proxy.ServeHTTP(c.Writer, c.Request) 38 | 39 | } else { 40 | // 服务自己处理 41 | c.JSON(http.StatusOK, gin.H{ 42 | "message": "mock server", 43 | }) 44 | } 45 | 46 | } 47 | 48 | func main() { 49 | 50 | r := gin.Default() 51 | 52 | // 创建路由 53 | r.Any("/*proxyPath", proxy) 54 | 55 | r.Run(":8085") 56 | } 57 | -------------------------------------------------------------------------------- /mock-hook/poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "click" 5 | version = "8.1.3" 6 | description = "Composable command line interface toolkit" 7 | optional = false 8 | python-versions = ">=3.7" 9 | files = [ 10 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 11 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 12 | ] 13 | 14 | [package.dependencies] 15 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 16 | 17 | [[package]] 18 | name = "colorama" 19 | version = "0.4.6" 20 | description = "Cross-platform colored terminal text." 21 | optional = false 22 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 23 | files = [ 24 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 25 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 26 | ] 27 | 28 | [metadata] 29 | lock-version = "2.0" 30 | python-versions = "^3.9" 31 | content-hash = "300514c376fd2b4fb2cd66777bf6cb3ef26ecf81acaf174322382728d5cd7cde" 32 | -------------------------------------------------------------------------------- /mock-hook/mock_hook/common.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import importlib 5 | 6 | 7 | def read_data(file_path: str = None) -> dict: 8 | """ 9 | read JSON data file. 10 | """ 11 | if file_path is None: 12 | return {} 13 | 14 | with open(file_path, "r+", encoding="utf-8") as json_file: 15 | req_data = json.load(json_file) 16 | return req_data 17 | 18 | 19 | def loader(func_name: str = "response_hook", file_name: str = "confrun.py", *args, **kwargs): 20 | """ 21 | Execute the hook function dynamically. 22 | :param func_name: function name 23 | :param file_name: hook file name 24 | """ 25 | # By default, confrun.py files are searched in the current directory 26 | file_dir = os.getcwd() 27 | sys.path.insert(0, file_dir) 28 | all_hook_files = list(filter(lambda x: x.endswith(file_name), os.listdir(file_dir))) 29 | all_hook_module = list(map(lambda x: x.replace(".py", ""), all_hook_files)) 30 | 31 | hooks = [] 32 | for module_name in all_hook_module: 33 | hooks.append(importlib.import_module(module_name)) 34 | 35 | # Execute the function according to the name 36 | for per_hook in hooks: 37 | try: 38 | func = getattr(per_hook, func_name) 39 | return func(*args, **kwargs) 40 | except AttributeError: 41 | return None 42 | 43 | -------------------------------------------------------------------------------- /learnAPI/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.4.5 9 | 10 | 11 | com.java 12 | learnAPI 13 | 0.0.1-SNAPSHOT 14 | learnAPI 15 | Demo project for Spring Boot 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /chat_robot/chat_server.py: -------------------------------------------------------------------------------- 1 | import sanic 2 | import httpx 3 | from sanic import Sanic 4 | from sanic.response import json 5 | from sanic.websocket import WebSocketProtocol 6 | from sanic.exceptions import NotFound 7 | from sanic.response import html 8 | from jinja2 import Environment, PackageLoader 9 | 10 | 11 | env = Environment(loader=PackageLoader('app', 'templates')) 12 | 13 | app = Sanic(__name__) 14 | 15 | 16 | @app.route('/') 17 | async def index(request): 18 | """ 19 | 聊天页面 20 | """ 21 | template = env.get_template('index.html') 22 | html_content = template.render(title='聊天机器人') 23 | return html(html_content) 24 | 25 | 26 | @app.websocket('/chat') 27 | async def chat(request, ws): 28 | """ 29 | 处理聊天信息,并返回消息 30 | :param request: 31 | :param ws: 32 | :return: 33 | """ 34 | while True: 35 | user_msg = await ws.recv() 36 | print('Received: ' + user_msg) 37 | intelligence_data = {"key": "free", "appid": 0, "msg": user_msg} 38 | r = httpx.get("http://api.qingyunke.com/api.php", params=intelligence_data) 39 | chat_msg = r.json()["content"] 40 | print('Sending: ' + chat_msg) 41 | await ws.send(chat_msg) 42 | 43 | 44 | if __name__ == "__main__": 45 | app.error_handler.add( 46 | NotFound, 47 | lambda r, e: sanic.response.empty(status=404) 48 | ) 49 | app.run(host="127.0.0.1", port=8000, protocol=WebSocketProtocol, debug=True) 50 | 51 | -------------------------------------------------------------------------------- /flask_api/tests/api_test_AES.py: -------------------------------------------------------------------------------- 1 | 2 | from Crypto.Cipher import AES 3 | import base64 4 | import requests 5 | import unittest 6 | import json 7 | 8 | 9 | class AESTest(unittest.TestCase): 10 | 11 | def setUp(self): 12 | # 字符串自动补全16倍数 13 | BS = 16 14 | self.pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) 15 | 16 | self.base_url = "http://127.0.0.1:5000/aes/" 17 | 18 | def encryptBase64(self,src): 19 | """ 20 | 生成 base64 字符串 21 | """ 22 | return base64.urlsafe_b64encode(src) 23 | 24 | def encryptAES(self, src): 25 | """ 26 | 生成AES密文 27 | """ 28 | key = b'W7v4D60fds2Cmk2U' 29 | iv = b"1172311105789011" 30 | cryptor = AES.new(key, AES.MODE_CBC, iv) 31 | src_str = self.pad(src) 32 | src_byte = src_str.encode('utf-8') 33 | ciphertext = cryptor.encrypt(src_byte) # AES加密 34 | aes_base64 = self.encryptBase64(ciphertext) # base64 二次加密 35 | return aes_base64 36 | 37 | def test_aes_interface(self): 38 | '''test AES interface''' 39 | payload = {'eid':1, 'phone':'18611001100'} 40 | data = json.dumps(payload) 41 | # 加密 42 | encoded = self.encryptAES(data).decode() 43 | 44 | r = requests.post(self.base_url, data={"data": encoded}) 45 | result = r.json() 46 | print(result) 47 | 48 | 49 | if __name__ == '__main__': 50 | unittest.main() -------------------------------------------------------------------------------- /goapi/Controllers/api_server.go: -------------------------------------------------------------------------------- 1 | package Controllers 2 | 3 | /* 4 | 参考:https://geektutu.com/post/quick-go-gin.html 5 | */ 6 | 7 | import ( 8 | "fmt" 9 | "github.com/gin-gonic/gin" 10 | "goapi/Controllers/common" 11 | "net/http" 12 | "reflect" 13 | ) 14 | 15 | func Hello(c *gin.Context) { 16 | c.JSON(http.StatusOK, gin.H{ 17 | "code": "10200", 18 | "message": "success", 19 | "data": "hello gin!", 20 | }) 21 | 22 | } 23 | 24 | func HelloName(c *gin.Context) { 25 | name := c.Param("name") 26 | c.JSON(http.StatusOK, gin.H{ 27 | "code": "10200", 28 | "message": "success", 29 | "data": "hello, " + name, 30 | }) 31 | } 32 | 33 | func UserInfo(c *gin.Context) { 34 | name := c.Query("name") 35 | role := c.DefaultQuery("role", "tearcher") 36 | c.JSON(http.StatusOK, gin.H{ 37 | "code": "10200", 38 | "message": "success", 39 | "data": name + " is a " + role, 40 | }) 41 | } 42 | 43 | func Login(c *gin.Context) { 44 | username := c.PostForm("username") 45 | password := c.PostForm("password") 46 | 47 | //判断返回值类型 48 | fmt.Println(reflect.TypeOf(username)) 49 | fmt.Println(reflect.TypeOf(password)) 50 | 51 | if username == "" || password == "" { 52 | c.JSON(http.StatusOK, common.Response("10101", "username or password null", "")) 53 | } else if username != "admin" || password != "123" { 54 | c.JSON(http.StatusOK, common.Response("10102", "username or password error", "")) 55 | } else { 56 | c.JSON(http.StatusOK, common.Response("10200", "success", "")) 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /goapi/go.mod: -------------------------------------------------------------------------------- 1 | module goapi 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.9.1 7 | github.com/stretchr/testify v1.8.3 8 | ) 9 | 10 | require ( 11 | github.com/bytedance/sonic v1.9.1 // indirect 12 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 15 | github.com/gin-contrib/sse v0.1.0 // indirect 16 | github.com/go-playground/locales v0.14.1 // indirect 17 | github.com/go-playground/universal-translator v0.18.1 // indirect 18 | github.com/go-playground/validator/v10 v10.14.0 // indirect 19 | github.com/goccy/go-json v0.10.2 // indirect 20 | github.com/json-iterator/go v1.1.12 // indirect 21 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 22 | github.com/leodido/go-urn v1.2.4 // indirect 23 | github.com/mattn/go-isatty v0.0.19 // indirect 24 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 25 | github.com/modern-go/reflect2 v1.0.2 // indirect 26 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 27 | github.com/pmezard/go-difflib v1.0.0 // indirect 28 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 29 | github.com/ugorji/go/codec v1.2.11 // indirect 30 | golang.org/x/arch v0.3.0 // indirect 31 | golang.org/x/crypto v0.9.0 // indirect 32 | golang.org/x/net v0.10.0 // indirect 33 | golang.org/x/sys v0.8.0 // indirect 34 | golang.org/x/text v0.9.0 // indirect 35 | google.golang.org/protobuf v1.30.0 // indirect 36 | gopkg.in/yaml.v3 v3.0.1 // indirect 37 | ) 38 | -------------------------------------------------------------------------------- /graphql_demo/schema.py: -------------------------------------------------------------------------------- 1 | from graphql.type.scalars import GraphQLString 2 | from graphql.type.schema import GraphQLSchema 3 | from graphql.type.definition import GraphQLArgument, GraphQLField, GraphQLNonNull, GraphQLObjectType 4 | 5 | 6 | def resolve_raises(*_): 7 | raise Exception("Throws!") 8 | 9 | 10 | QueryRootType = GraphQLObjectType( 11 | name="QueryRoot", 12 | fields={ 13 | "thrower": GraphQLField(GraphQLNonNull(GraphQLString), resolve=resolve_raises), 14 | "request": GraphQLField( 15 | GraphQLNonNull(GraphQLString), 16 | resolve=lambda obj, info: info.context["request"].args.get("q"), 17 | ), 18 | "context": GraphQLField( 19 | GraphQLObjectType( 20 | name="context", 21 | fields={ 22 | "session": GraphQLField(GraphQLString), 23 | "request": GraphQLField( 24 | GraphQLNonNull(GraphQLString), 25 | resolve=lambda obj, info: info.context["request"], 26 | ), 27 | }, 28 | ), 29 | resolve=lambda obj, info: info.context, 30 | ), 31 | "test": GraphQLField( 32 | type_=GraphQLString, 33 | args={"who": GraphQLArgument(GraphQLString)}, 34 | resolve=lambda obj, info, who="World": "Hello %s" % who, 35 | ), 36 | }, 37 | ) 38 | 39 | MutationRootType = GraphQLObjectType( 40 | name="MutationRoot", 41 | fields={ 42 | "writeTest": GraphQLField(type_=QueryRootType, resolve=lambda *_: QueryRootType) 43 | }, 44 | ) 45 | 46 | Schema = GraphQLSchema(QueryRootType, MutationRootType) 47 | -------------------------------------------------------------------------------- /flask_api/tests/api_test_sign.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | from time import time 4 | import hashlib 5 | 6 | 7 | class SignTest(unittest.TestCase): 8 | 9 | 10 | def setUp(self): 11 | self.base_url = "http://127.0.0.1:5000/sign/" 12 | # app_key 13 | self.api_key = "&Guest-Bugmaster" 14 | # 当前时间 15 | now_time = time() 16 | self.client_time = str(now_time).split('.')[0] 17 | # sign 18 | md5 = hashlib.md5() 19 | sign_str = self.client_time + self.api_key 20 | sign_bytes_utf8 = sign_str.encode(encoding="utf-8") 21 | md5.update(sign_bytes_utf8) 22 | self.sign_md5 = md5.hexdigest() 23 | 24 | def tearDown(self): 25 | print(self.result) 26 | 27 | def test_sign_null(self): 28 | """ 29 | sign=null 签名参数为空 30 | """ 31 | now_time = str(int(self.client_time)) 32 | payload = {'time': now_time, 'sign': ''} 33 | r = requests.post(self.base_url, data=payload) 34 | self.result = r.json() 35 | 36 | 37 | def test_sign_error(self): 38 | """ 39 | sign='error' 签名参数错误 40 | """ 41 | now_time = str(int(self.client_time)) 42 | payload = {'time': now_time, 'sign': 'error'} 43 | r = requests.post(self.base_url, data=payload) 44 | self.result = r.json() 45 | 46 | 47 | def test_sign_timeout(self): 48 | """ 49 | 签名参数超时 50 | """ 51 | now_time = str(int(self.client_time) - 61) 52 | payload = {'eid': 1, '': '', 'limit': '', 'address': '', 53 | 'start_time': '', 'time': now_time, 'sign': 'error'} 54 | r = requests.post(self.base_url, data=payload) 55 | self.result = r.json() 56 | 57 | 58 | def test_sign_ok(self): 59 | """ 60 | 签名参数正确 61 | """ 62 | now_time = str(int(self.client_time)) 63 | payload = {'time': now_time, 'sign': self.sign_md5} 64 | r = requests.post(self.base_url, data=payload) 65 | self.result = r.json() 66 | 67 | 68 | 69 | if __name__ == '__main__': 70 | unittest.main() 71 | -------------------------------------------------------------------------------- /sys_monitoring/websocket_server.py: -------------------------------------------------------------------------------- 1 | """ 2 | 实现前后端webSockets长连接。 3 | """ 4 | import time 5 | import math 6 | import psutil 7 | from random import randint 8 | from flask import Flask, render_template, session, request 9 | from flask_socketio import SocketIO, emit 10 | from threading import Lock 11 | 12 | app = Flask(__name__, template_folder='./') 13 | app.config['SECRET_KEY'] = 'secret!' 14 | socketio = SocketIO(app, cors_allowed_origins='*') 15 | 16 | cpu_thread = None 17 | cpu_thread_lock = Lock() 18 | menery_thread = None 19 | menery_thread_lock = Lock() 20 | 21 | 22 | @app.route('/') 23 | def index(): 24 | """ 25 | Web页面 26 | """ 27 | return render_template('index.html') 28 | 29 | 30 | @socketio.on('connect', namespace='/get_cpu') 31 | def cpu_connect(): 32 | global cpu_thread 33 | with cpu_thread_lock: 34 | if cpu_thread is None: 35 | cpu_thread = socketio.start_background_task(target=cpu_background_thread) 36 | 37 | 38 | def cpu_background_thread(): 39 | count = 0 40 | while True: 41 | count += 1 42 | socketio.sleep(5) 43 | t = time.strftime('%H:%M:%S', time.localtime()) 44 | cpu = psutil.cpu_percent(interval=None, percpu=True) 45 | print("t", t) 46 | print("cpu-->", t, cpu) 47 | socketio.emit('server_response', 48 | {'data': [t, cpu], 'count': count}, namespace='/get_cpu') 49 | 50 | 51 | @socketio.on('connect', namespace='/get_memory') 52 | def memory_connect(): 53 | global menery_thread 54 | with menery_thread_lock: 55 | if menery_thread is None: 56 | menery_thread = socketio.start_background_task(target=memory_background_thread) 57 | 58 | 59 | def memory_background_thread(): 60 | count = 0 61 | while True: 62 | count += 1 63 | socketio.sleep(5) 64 | t = time.strftime('%H:%M:%S', time.localtime()) 65 | memory = psutil.virtual_memory() 66 | print("t", t) 67 | print("memory-->", t, type(memory), memory) 68 | used_mem = math.ceil(memory.used / (1024 * 1024)) 69 | percent_mem = memory.percent 70 | socketio.emit('server_response', 71 | {'data': [t, used_mem], 'count': count, 'percent': percent_mem}, namespace='/get_memory') 72 | 73 | 74 | if __name__ == '__main__': 75 | socketio.run(app, host='127.0.0.1', port=9090, debug=True) 76 | -------------------------------------------------------------------------------- /chat_robot/app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Chat Room 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {{title}} 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 发送 39 | 40 | 41 | 42 | 43 | 44 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /goapi/README.md: -------------------------------------------------------------------------------- 1 | # API base golang gin web framework 2 | 3 | ## go 环境安装 4 | 5 | 1. 下载golang 6 | https://golang.org/doc/install 7 | 8 | 查看go版本 9 | 10 | ```shell 11 | > go version 12 | go version go1.15.2 darwin/amd64 13 | ``` 14 | 15 | 2. 配置环境:GOROOT、GOPATH等 16 | 17 | ```shell 18 | > go env 19 | 20 | GO111MODULE="on" 21 | GOARCH="amd64" 22 | GOBIN="" 23 | GOCACHE="/Users/fnngj/Library/Caches/go-build" 24 | GOENV="/Users/fnngj/Library/Application Support/go/env" 25 | GOEXE="" 26 | GOFLAGS="" 27 | GOHOSTARCH="amd64" 28 | GOHOSTOS="darwin" 29 | GOINSECURE="" 30 | GOMODCACHE="/Users/fnngj/go/pkg/mod" 31 | GONOPROXY="" 32 | GONOSUMDB="" 33 | GOOS="darwin" 34 | GOPATH="/Users/fnngj/go" 35 | GOPRIVATE="" 36 | GOPROXY="https://mirrors.aliyun.com/goproxy/" 37 | GOROOT="/usr/local/go" 38 | GOSUMDB="sum.golang.org" 39 | GOTMPDIR="" 40 | GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64" 41 | GCCGO="gccgo" 42 | AR="ar" 43 | CC="clang" 44 | CXX="clang++" 45 | CGO_ENABLED="1" 46 | GOMOD="/Users/fnngj/go/src/godemo/go.mod" 47 | CGO_CFLAGS="-g -O2" 48 | CGO_CPPFLAGS="" 49 | CGO_CXXFLAGS="-g -O2" 50 | CGO_FFLAGS="-g -O2" 51 | CGO_LDFLAGS="-g -O2" 52 | PKG_CONFIG="pkg-config" 53 | GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/gj/tg9pvv8n5vd15sjtf5ndh7zh0000gn/T/go-build364476694=/tmp/go-build -gno-record-gcc-switches -fno-common" 54 | ``` 55 | 56 | 3. 设置国内源 57 | 58 | 参考:https://goproxy.cn/ 59 | 60 | ```shell 61 | > go env -w GO111MODULE=on 62 | 63 | > go env -w GOPROXY=https://goproxy.cn,direct 64 | ``` 65 | 66 | 默认管理非官方库在 `$GOPATH\src\`,开启了 GO111MODULE 之后,此时将会到`$GOPATH\src\pkg\` 目录下。 67 | 通过 GOPROXY 修改代理为国内源。 68 | 69 | 4. 设置 go mod 70 | 71 | 使用`go mod` 管理项目,就不需要非得把项目放到`$GOPATH` 指定目录下,你可以在你磁盘的任何位置新建一个项目。 72 | 73 | ```shell 74 | > cd goapi # 进入web项目目录 75 | > go mod tidy 76 | ``` 77 | 78 | 5. 安装gin 79 | 80 | github地址:https://github.com/gin-gonic/gin 81 | 82 | ```shell 83 | > go get -u github.com/gin-gonic/gin 84 | ``` 85 | 86 | ## 创建 gin 项目 87 | 88 | 架构参考:https://www.jianshu.com/p/92919004293d 89 | 90 | 91 | 1. 运行`main.go`文件. 92 | 93 | ```shell 94 | > go run main.go 95 | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. 96 | 97 | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. 98 | - using env: export GIN_MODE=release 99 | - using code: gin.SetMode(gin.ReleaseMode) 100 | 101 | [GIN-debug] GET /hello --> main.main.func1 (3 handlers) 102 | [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default 103 | [GIN-debug] Listening and serving HTTP on :8080 104 | ``` 105 | 106 | 3. 通过浏览器访问:`http://127.0.0.1:8080` 107 | 108 | ```json 109 | { 110 | code: "10200", 111 | data: "hello gin!", 112 | message: "success" 113 | } 114 | ``` 115 | -------------------------------------------------------------------------------- /goapi/mock_api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "time" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func main() { 15 | r := gin.Default() 16 | 17 | r.GET("/mock/:number/*subpath", func(c *gin.Context) { 18 | /* 19 | 支持 subpath URL 20 | */ 21 | number := c.Param("number") 22 | subpath := c.Param("subpath") 23 | c.JSON(http.StatusOK, gin.H{ 24 | "number": number, 25 | "subpath": subpath, 26 | }) 27 | }) 28 | 29 | r.GET("/check/status_code", func(c *gin.Context) { 30 | /* 31 | 支持非200状态码 32 | http.StatusNotImplemented 指定 501 33 | */ 34 | c.JSON(http.StatusNotImplemented, gin.H{ 35 | "message": "状态码 501", 36 | }) 37 | }) 38 | 39 | r.GET("/check/delay", func(c *gin.Context) { 40 | /* 41 | 支持延迟返回 42 | 3s 后返回结果 43 | */ 44 | var sec int = 3 45 | time.Sleep(time.Duration(sec) * time.Second) 46 | c.JSON(http.StatusOK, gin.H{ 47 | "message": "delay 3s", 48 | }) 49 | }) 50 | 51 | r.GET("/check/header", func(c *gin.Context) { 52 | /* 53 | 支持 header请求头 54 | */ 55 | var header = make(map[string]string) 56 | header["token"] = "" 57 | header["user"] = "" 58 | 59 | // 从header请求头中提取参数值 60 | for k, _ := range header { 61 | i := c.Request.Header.Get(k) 62 | header[k] = i 63 | } 64 | 65 | fmt.Printf("%+v\n", header) 66 | 67 | c.JSON(http.StatusOK, gin.H{ 68 | "message": "ok", 69 | "header": header, 70 | }) 71 | }) 72 | 73 | r.GET("/check/params", func(c *gin.Context) { 74 | /* 75 | 支持 GET params 参数 76 | */ 77 | var param = make(map[string]string) 78 | param["id"] = "" 79 | param["name"] = "" 80 | param["is_delete"] = "" 81 | 82 | // 从请求URL中提取参数值 83 | for k, _ := range param { 84 | i := c.Query(k) 85 | param[k] = i 86 | } 87 | 88 | fmt.Printf("%+v\n", param) 89 | 90 | c.JSON(http.StatusOK, gin.H{ 91 | "message": "params", 92 | "param": param, 93 | }) 94 | }) 95 | 96 | r.POST("/check/data", func(c *gin.Context) { 97 | /* 98 | 支持 POST from-datas/x-www-form-urlencoded 参数 99 | */ 100 | var param = make(map[string]string) 101 | param["id"] = "" 102 | param["name"] = "" 103 | 104 | // 从请求URL中提取参数值 105 | for k, _ := range param { 106 | value := c.PostForm(k) 107 | param[k] = value 108 | } 109 | 110 | fmt.Printf("%+v\n", param) 111 | 112 | c.JSON(http.StatusOK, gin.H{ 113 | "message": "params", 114 | "data": param, 115 | }) 116 | }) 117 | 118 | r.POST("/check/json", func(c *gin.Context) { 119 | /* 120 | 支持 POST json 参数 121 | */ 122 | json := make(map[string]interface{}) //注意该结构接受的内容 123 | c.BindJSON(&json) 124 | log.Printf("%v\n", &json) 125 | 126 | c.JSON(http.StatusOK, gin.H{ 127 | "message": "params", 128 | "json": &json, 129 | }) 130 | }) 131 | 132 | r.POST("check/xml", func(c *gin.Context) { 133 | /* 134 | 支持 body XML 格式数据 135 | */ 136 | body, _ := ioutil.ReadAll(c.Request.Body) 137 | println("body XML", string(body)) 138 | 139 | // 查看完的数据重新放回去 140 | c.Request.Body = ioutil.NopCloser(bytes.NewReader(body)) 141 | 142 | // 返回 XML 143 | // c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK}) 144 | // 返回 string 145 | c.String(http.StatusOK, string(body)) 146 | }) 147 | 148 | r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") 149 | } 150 | -------------------------------------------------------------------------------- /fastapi_sample/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | auth: bugmaster 3 | data: 2021-06-26 4 | """ 5 | from enum import Enum 6 | from typing import Optional 7 | from fastapi import FastAPI 8 | from fastapi import Form 9 | from fastapi import File, UploadFile 10 | from fastapi import Cookie, Header 11 | from pydantic import BaseModel 12 | 13 | app = FastAPI() 14 | 15 | 16 | ALLOWED_EXTENSIONS = ['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', "json"] 17 | 18 | 19 | @app.get("/") 20 | def read_root(): 21 | return {"Hello": "World"} 22 | 23 | 24 | @app.get("/items/{item_id}") 25 | def read_item(item_id: int, q: Optional[str] = None, short: bool = False): 26 | item = {"item_id": item_id} 27 | if q: 28 | return {"item_id": item_id, "q": q} 29 | if not short: 30 | item.update({ 31 | "description": "This is an amazing item that has a long description" 32 | }) 33 | return item 34 | 35 | 36 | class Item(BaseModel): 37 | name: str 38 | price: float 39 | is_offer: Optional[bool] = None # 可选参数 40 | 41 | 42 | @app.put("/items/{item_id}") 43 | def update_item(item_id: int, item: Item): 44 | return {"item_name": item.name, "item_id": item_id} 45 | 46 | 47 | class ModelName(str, Enum): 48 | alexnet = "alexnet" 49 | resnet = "resnet" 50 | lenet = "lenet" 51 | 52 | 53 | @app.get("/models/{model_name}") 54 | async def get_model(model_name: ModelName): 55 | if model_name == ModelName.alexnet: 56 | return {"model_name": model_name, "message": "Deep Learning FTW!"} 57 | if model_name.value == "lenet": 58 | return {"model_name": model_name, "message": "LeCNN all the images"} 59 | return {"model_name": model_name, "message": "Have some residuals"} 60 | 61 | 62 | fake_items_db = [ 63 | {"item_name": "apple"}, 64 | {"item_name": "banana"}, 65 | {"item_name": "orange"}, 66 | {"item_name": "watermelon"}, 67 | {"item_name": "grape"} 68 | ] 69 | 70 | 71 | @app.get("/items/") 72 | def read_item(start: int = 0, length: int = 10): 73 | return fake_items_db[start : start + length] 74 | 75 | 76 | @app.get("/users/{user_id}/items/{item_id}") 77 | async def read_user_item(user_id: int, item_id: str, q: Optional[str] = None, short: bool = False): 78 | item = {"item_id": item_id, "owner_id": user_id} 79 | if q: 80 | item.update({"q": q}) 81 | if not short: 82 | item.update( 83 | {"description": "This is an amazing item that has a long description"} 84 | ) 85 | return item 86 | 87 | 88 | class Commodity(BaseModel): 89 | name: str 90 | description: Optional[str] = None 91 | price: float 92 | tax: Optional[float] = None 93 | 94 | 95 | @app.post("/commodity/") 96 | async def create_commodity(commodity: Commodity): 97 | commodity_dict = commodity.dict() 98 | if commodity.tax: 99 | price_with_tax = commodity.price + commodity.tax 100 | commodity_dict.update({"price_with_tax": price_with_tax}) 101 | return commodity_dict 102 | 103 | 104 | # from-data 参数 105 | @app.post("/login/") 106 | async def login(*, username: str = Form(...), password: str = Form(...)): 107 | return {"username": username, "password": password} 108 | 109 | 110 | # 文件后缀名的判断 111 | def allowed_file(filename): 112 | return '.' in filename and \ 113 | filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS 114 | 115 | 116 | # 文件上传 117 | @app.post("/files/") 118 | async def create_file(file: UploadFile = File(...)): 119 | if file and allowed_file(file.filename): 120 | contents = await file.read() 121 | with open("./upload/"+file.filename, "wb") as f: 122 | f.write(contents) 123 | return {"success": True, "filename": file.filename} 124 | else: 125 | return {"success": False, "message": "file type nonsupport"} 126 | 127 | 128 | # Cookie 129 | @app.get("/cookies/") 130 | async def read_cookie(ads_id: Optional[str] = Cookie(None)): 131 | return {"ads_id": ads_id} 132 | 133 | 134 | # Header 135 | @app.get("/headers/") 136 | async def read_headers(user_agent: Optional[str] = Header(None)): 137 | return {"User-Agent": user_agent} 138 | 139 | 140 | -------------------------------------------------------------------------------- /learnAPI/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /flask_api/tests/api_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import requests 4 | 5 | 6 | class BaseCase(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.base_url = "http://127.0.0.1:5000" 10 | 11 | 12 | class HttpbinTest(BaseCase): 13 | 14 | def test_get(self): 15 | r = requests.get(self.base_url + "/get", params={"id": 1}) 16 | result = r.json() 17 | print(result) 18 | 19 | def test_post(self): 20 | r = requests.post(self.base_url + "/post", data={"key": "value"}) 21 | result = r.json() 22 | print(result) 23 | 24 | def test_put(self): 25 | r = requests.put(self.base_url + "/put", json={"key": "value"}) 26 | result = r.json() 27 | print(result) 28 | 29 | def test_delete(self): 30 | r = requests.delete(self.base_url + "/delete", data={"id": 1}) 31 | result = r.json() 32 | print(result) 33 | 34 | 35 | class SampleTest(BaseCase): 36 | 37 | def test_sample(self): 38 | r = requests.get(self.base_url) 39 | result = r.json() 40 | print(result) 41 | 42 | 43 | class SESTfulGetTest(BaseCase): 44 | 45 | def test_sample(self): 46 | name = "tom" 47 | r = requests.get(self.base_url + "/user/" + name) 48 | result = r.json() 49 | print(result) 50 | 51 | 52 | class GetUserDataTest(BaseCase): 53 | 54 | def test_uid_null(self): 55 | uid = "2" 56 | r = requests.get(self.base_url + "/id/" + uid) 57 | result = r.json() 58 | print(result) 59 | 60 | def test_uid_exist(self): 61 | uid = "1" 62 | r = requests.get(self.base_url + "/id/" + uid) 63 | result = r.json() 64 | print(result) 65 | 66 | 67 | class GetSampleTest(BaseCase): 68 | 69 | def test_sample_1(self): 70 | payload = {'q': 'selenium'} 71 | r = requests.get(self.base_url + "/search/", params=payload) 72 | result = r.json() 73 | print(result) 74 | 75 | def test_sample_2(self): 76 | r = requests.get(self.base_url + "/search/?q=selenium") 77 | result = r.json() 78 | print(result) 79 | 80 | 81 | class POSTSampleTest(BaseCase): 82 | 83 | def test_sample_1(self): 84 | r = requests.post(self.base_url + "/login") 85 | result = r.json() 86 | print(result) 87 | 88 | def test_sample_2(self): 89 | payload = {'username': '', "password": ""} 90 | r = requests.post(self.base_url + "/login", data=payload) 91 | result = r.json() 92 | print(result) 93 | 94 | def test_sample_3(self): 95 | payload = {'username': 'abc', "password": "123"} 96 | r = requests.post(self.base_url + "/login", data=payload) 97 | result = r.json() 98 | print(result) 99 | 100 | def test_sample_4(self): 101 | payload = {'username': 'admin', "password": "a123456"} 102 | r = requests.post(self.base_url + "/login", data=payload) 103 | result = r.json() 104 | print(result) 105 | 106 | 107 | class POSTJsonTest(BaseCase): 108 | 109 | def test_key_null(self): 110 | payload = {} 111 | r = requests.post(self.base_url + "/add_user", json=payload) 112 | result = r.json() 113 | print(result) 114 | 115 | def test_name_null(self): 116 | payload = {"name": "", "age": 22, "height": 177} 117 | r = requests.post(self.base_url + "/add_user", json=payload) 118 | result = r.json() 119 | print(result) 120 | 121 | def test_name_exist(self): 122 | payload = {"name": "tom", "age": 22, "height": 177} 123 | r = requests.post(self.base_url + "/add_user", json=payload) 124 | result = r.json() 125 | print(result) 126 | 127 | def test_add_success(self): 128 | payload = {'name': 'jack', "age": 22, "height": 177} 129 | r = requests.post(self.base_url + "/add_user", json=payload) 130 | result = r.json() 131 | print(result) 132 | 133 | 134 | class HeadersTest(BaseCase): 135 | 136 | def test_smaple(self): 137 | headers = {'Content-Type': 'application/json', 138 | "token": "3d80caXELzU1aWmHwxl0TzW7jtterObm8l5EeAfipnhyaKmhFl8KdhFRvy4"} 139 | r = requests.post(self.base_url + "/header", headers=headers) 140 | result = r.json() 141 | print(result) 142 | 143 | 144 | class AuthTest(BaseCase): 145 | 146 | def test_auth_none(self): 147 | r = requests.post(self.base_url + "/auth") 148 | result = r.json() 149 | print(result) 150 | 151 | def test_auth_null(self): 152 | auth = ("", "") 153 | r = requests.post(self.base_url + "/auth", auth=auth) 154 | result = r.json() 155 | print(result) 156 | 157 | def test_auth_fail(self): 158 | auth = ("abc", "123") 159 | r = requests.post(self.base_url + "/auth", auth=auth) 160 | result = r.json() 161 | print(result) 162 | 163 | def test_auth_success(self): 164 | auth = ("admin", "admin123") 165 | r = requests.post(self.base_url + "/auth", auth=auth) 166 | result = r.json() 167 | print(result) 168 | 169 | 170 | class UploadFileTest(BaseCase): 171 | 172 | def test_sample(self): 173 | with open('file.txt', 'rb') as f: 174 | files = {'file': f} 175 | r = requests.post(self.base_url + "/upload", files=files) 176 | result = r.json() 177 | print(result) 178 | 179 | 180 | class MoreMethodTest(BaseCase): 181 | 182 | def test_get_method(self): 183 | r = requests.get(self.base_url + "/phone/1") 184 | result = r.json() 185 | print(result) 186 | 187 | def test_put_method(self): 188 | data = {"name": "华为手机", "price": "3999"} 189 | r = requests.put(self.base_url + "/phone/1", data=data) 190 | result = r.json() 191 | print(result) 192 | 193 | def test_delete_method(self): 194 | r = requests.delete(self.base_url + "/phone/1") 195 | result = r.json() 196 | print(result) 197 | 198 | 199 | class SessionTest(BaseCase): 200 | 201 | def test_sample(self): 202 | s = requests.Session() 203 | r = s.post(self.base_url + "/user_login", data={"username": "jack", "password": "123"}) 204 | result = r.json() 205 | print(result) 206 | r2 = s.get(self.base_url + "/user_data") 207 | result2 = r2.json() 208 | print(result2) 209 | 210 | 211 | class DependencyTest(BaseCase): 212 | 213 | def test_sample(self): 214 | r = requests.get(self.base_url + "/get_activity") 215 | result = r.json() 216 | print(result) 217 | activity_id = result["data"]["id"] 218 | 219 | r = requests.get(self.base_url + "/get_user") 220 | result = r.json() 221 | print(result) 222 | user_id = result["data"]["id"] 223 | 224 | data = {"aid": activity_id, "uid": user_id} 225 | r = requests.post(self.base_url + "/lucky_number", data=data) 226 | result = r.json() 227 | print(result) 228 | 229 | 230 | if __name__ == '__main__': 231 | unittest.main() 232 | -------------------------------------------------------------------------------- /learnAPI/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /goapi/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 11 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 14 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 15 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 16 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 17 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 18 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 19 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 20 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 21 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 22 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 23 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 24 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 25 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 26 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 27 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 28 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 29 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 30 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 31 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 32 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 33 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 34 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 35 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 36 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 37 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 38 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 39 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 40 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 41 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 42 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 43 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 44 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 45 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 46 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 47 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 48 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 49 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 50 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 51 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 52 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 53 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 54 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 55 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 56 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 57 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 58 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 59 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 60 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 61 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 62 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 63 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 64 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 65 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 66 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 67 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 68 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 69 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 70 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 72 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 74 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 75 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 76 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 77 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 78 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 79 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 80 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 81 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 82 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 83 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 84 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 85 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 86 | -------------------------------------------------------------------------------- /learnAPI/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /flask_api/README.md: -------------------------------------------------------------------------------- 1 | # learning-API-test 2 | 3 | 帮助你学习API接口测试。 4 | 5 | ## 开始 6 | 7 | * __安装__ 8 | 9 | 克隆或下载项目,安装依赖。 10 | 11 | ```shell 12 | $ pip install -r requirements.txt 13 | ``` 14 | 15 | * __启动接口服务__ 16 | 17 | ```shell 18 | $ flask run 19 | 20 | * Serving Flask app 'api_server' 21 | * Debug mode: off 22 | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. 23 | * Running on http://127.0.0.1:5000 24 | Press CTRL+C to quit 25 | ``` 26 | 27 | * 其他启动参数 28 | 29 | ```shell 30 | # 指定启动文件 31 | $ flask --app api_server run 32 | 33 | # 指定IP 34 | flask run --host=0.0.0.0 35 | 36 | # 指定debug模式 37 | flask --app api_server run --debug 38 | ``` 39 | 40 | Flask Web框架可以非常简单的方式实现API,项目中的所有API都在`api_server.py` 文件中。 41 | 42 | * __接口测试库__ 43 | 44 | 所有例子使用[Requests](https://2.python-requests.org//zh_CN/latest/user/quickstart.html) 45 | 库调用,你也可以使用其他API测试工具,如Postman、JMeter等。 46 | 47 | ## http接口的基本信息 48 | 49 | * URL (http://www.xxx.com/v1/login) 50 | * 方法:GET/POST/PUT/DELETE 51 | * Auth 52 | * Header 53 | * 参数类型(form-data/json..) 54 | * 参数值(id=1,name=tom) 55 | * 参数加密方式 56 | * 返回值(错误码/提示信息/数据) 57 | 58 | ## 接口测试例子 59 | 60 | ### Httpbin API 61 | 62 | ```python 63 | import requests 64 | 65 | base_url = "http://127.0.0.1:5000/" 66 | 67 | r = requests.get(base_url + "/get", params={"id": 1}) 68 | result = r.json() 69 | print(result) 70 | 71 | r = requests.post(base_url + "/post", data={"key": "value"}) 72 | result = r.json() 73 | print(result) 74 | 75 | r = requests.put(base_url + "/put", json={"key": "value"}) 76 | result = r.json() 77 | print(result) 78 | 79 | r = requests.delete(base_url + "/delete", data={"id": 1}) 80 | result = r.json() 81 | print(result) 82 | ``` 83 | 84 | 返回结果: 85 | 86 | ```json 87 | [ 88 | { 89 | "args": { 90 | "id": "1" 91 | }, 92 | "headers": { 93 | "Accept": "*/*", 94 | "Accept-Encoding": "gzip, deflate", 95 | "Connection": "keep-alive", 96 | "Host": "127.0.0.1:5000", 97 | "User-Agent": "python-requests/2.25.0" 98 | }, 99 | "method": "GET", 100 | "origin": "127.0.0.1", 101 | "url": "http://127.0.0.1:5000/get?id=1" 102 | }, 103 | { 104 | "args": {}, 105 | "data": "key=value", 106 | "form": { 107 | "key": "value" 108 | }, 109 | "headers": { 110 | "Accept": "*/*", 111 | "Accept-Encoding": "gzip, deflate", 112 | "Connection": "keep-alive", 113 | "Content-Length": "9", 114 | "Content-Type": "application/x-www-form-urlencoded", 115 | "Host": "127.0.0.1:5000", 116 | "User-Agent": "python-requests/2.25.0" 117 | }, 118 | "json": null, 119 | "method": "POST", 120 | "origin": "127.0.0.1", 121 | "url": "http://127.0.0.1:5000/post" 122 | }, 123 | { 124 | "args": {}, 125 | "data": "{\"key\": \"value\"}", 126 | "headers": { 127 | "Accept": "*/*", 128 | "Accept-Encoding": "gzip, deflate", 129 | "Connection": "keep-alive", 130 | "Content-Length": "16", 131 | "Content-Type": "application/json", 132 | "Host": "127.0.0.1:5000", 133 | "User-Agent": "python-requests/2.25.0" 134 | }, 135 | "json": { 136 | "key": "value" 137 | }, 138 | "method": "PUT", 139 | "origin": "127.0.0.1", 140 | "url": "http://127.0.0.1:5000/put" 141 | }, 142 | { 143 | "args": {}, 144 | "data": "id=1", 145 | "headers": { 146 | "Accept": "*/*", 147 | "Accept-Encoding": "gzip, deflate", 148 | "Connection": "keep-alive", 149 | "Content-Length": "4", 150 | "Content-Type": "application/x-www-form-urlencoded", 151 | "Host": "127.0.0.1:5000", 152 | "User-Agent": "python-requests/2.25.0" 153 | }, 154 | "json": null, 155 | "method": "DELETE", 156 | "origin": "127.0.0.1", 157 | "url": "http://127.0.0.1:5000/delete" 158 | } 159 | ] 160 | ``` 161 | 162 | ### 最简单的接口调用 163 | 164 | ```python 165 | import requests 166 | 167 | r = requests.get("http://127.0.0.1:5000/") 168 | result = r.json() 169 | print(result) 170 | ``` 171 | 172 | 返回结果: 173 | 174 | ```json 175 | { 176 | "code": 10200, 177 | "message": "Welcome to API testing" 178 | } 179 | ``` 180 | 181 | ### 累加器 182 | 183 | 每次请求数字加1 184 | 185 | ```python 186 | import requests 187 | 188 | r = requests.get("http://127.0.0.1:5000/add_one") 189 | result = r.json() 190 | print(result) 191 | ``` 192 | 193 | 返回结果: 194 | 195 | ```json 196 | { 197 | "code": 10200, 198 | "data": { 199 | "number": 1 200 | }, 201 | "message": "success" 202 | } 203 | ``` 204 | 205 | ### RESTful 风格的API 206 | 207 | ```python 208 | import requests 209 | 210 | name = "tom" 211 | r = requests.get("http://127.0.0.1:5000/user/" + name) 212 | result = r.json() 213 | print(result) 214 | ``` 215 | 216 | 返回结果: 217 | 218 | ```json 219 | { 220 | "code": 10200, 221 | "message": "hello, tom" 222 | } 223 | ``` 224 | 225 | ### 根据用户id返回不同的结果 226 | 227 | ```python 228 | import requests 229 | 230 | uid = "1" 231 | r = requests.get("http://127.0.0.1:5000/id/" + uid) 232 | result = r.json() 233 | print(result) 234 | 235 | ``` 236 | 237 | 返回结果: 238 | 239 | ```json 240 | [ 241 | { 242 | "code": 10101, 243 | "message": "user id null" 244 | }, 245 | { 246 | "code": 10200, 247 | "data": { 248 | "age": 22, 249 | "id": 1, 250 | "name": "tom" 251 | }, 252 | "message": "success" 253 | } 254 | ] 255 | ``` 256 | 257 | ### 一般GET请求 258 | 259 | * 方法一 260 | 261 | ```python 262 | import requests 263 | 264 | payload = {"q": "selenium"} 265 | r = requests.get("http://127.0.0.1:5000/search/", params=payload) 266 | result = r.json() 267 | print(result) 268 | ``` 269 | 270 | * 方式二 271 | 272 | ```python 273 | import requests 274 | 275 | r = requests.get("http://127.0.0.1:5000/search/?q=selenium") 276 | result = r.json() 277 | print(result) 278 | ``` 279 | 280 | 返回结果: 281 | 282 | ```json 283 | { 284 | "code": 10200, 285 | "data": [ 286 | "selenium教程", 287 | "seleniumhq.org", 288 | "selenium环境安装" 289 | ], 290 | "message": "success" 291 | } 292 | ``` 293 | 294 | ### POST请求 295 | 296 | 参数类型为:```from-data```/```x-www-from-urlencode```格式 297 | 298 | ```python 299 | import requests 300 | 301 | payload = {"username": "admin", "password": "a123456"} 302 | r = requests.post("http://127.0.0.1:5000/login", data=payload) 303 | result = r.json() 304 | print(result) 305 | ``` 306 | 307 | 返回结果: 308 | 309 | ```json 310 | [ 311 | { 312 | "code": 10102, 313 | "message": "username or password is None" 314 | }, 315 | { 316 | "code": 10103, 317 | "message": "username or password is null" 318 | }, 319 | { 320 | "code": 10104, 321 | "message": "username or password error" 322 | }, 323 | { 324 | "code": 10200, 325 | "message": "login success" 326 | } 327 | ] 328 | ``` 329 | 330 | #### POST请求 331 | 332 | 参数类型为:```JSON```格式 333 | 334 | ```python 335 | import requests 336 | 337 | payload = {"name": "jack", "age": 22, "height": 177} 338 | r = requests.post("http://127.0.0.1:5000/add_user", json=payload) 339 | result = r.json() 340 | print(result) 341 | ``` 342 | 343 | 返回结果: 344 | 345 | ```json 346 | [ 347 | { 348 | "code": 10102, 349 | "message": "key null" 350 | }, 351 | { 352 | "code": 10103, 353 | "message": "name null" 354 | }, 355 | { 356 | "code": 10104, 357 | "message": "name exist" 358 | }, 359 | { 360 | "code": 10105, 361 | "message": "format error" 362 | }, 363 | { 364 | "code": 10200, 365 | "message": "add success", 366 | "data": { 367 | "age": 22, 368 | "height": 177, 369 | "name": "jack" 370 | } 371 | } 372 | ] 373 | ``` 374 | 375 | ### 带Header的接口 376 | 377 | ```python 378 | import requests 379 | 380 | headers = {"Content-Type": "application/json", 381 | "token": "3d80caXELzU1aWmHwxl0TzW7jtterObm8l5EeAfipnhyaKmhFl8KdhFRvy4"} 382 | r = requests.post("http://127.0.0.1:5000/header", headers=headers) 383 | result = r.json() 384 | print(result) 385 | 386 | ``` 387 | 388 | 返回结果: 389 | 390 | ```json 391 | { 392 | "code": 10200, 393 | "message": "header ok!", 394 | "data": { 395 | "Content-Type": "application/json", 396 | "token": "3d80caXELzU1aWmHwxl0TzW7jtterObm8l5EeAfipnhyaKmhFl8KdhFRvy4" 397 | } 398 | } 399 | 400 | ``` 401 | 402 | ### 带Basic Auth认证的接口 403 | 404 | ```python 405 | import requests 406 | 407 | auth = ("admin", "admin123") 408 | r = requests.post("http://127.0.0.1:5000/auth", auth=auth) 409 | result = r.json() 410 | print(result) 411 | 412 | ``` 413 | 414 | 返回结果: 415 | 416 | ```json 417 | [ 418 | { 419 | "code": 10101, 420 | "message": "Authorization None" 421 | }, 422 | { 423 | "code": 10102, 424 | "message": "Authorization null" 425 | }, 426 | { 427 | "code": 10103, 428 | "message": "Authorization fail!" 429 | }, 430 | { 431 | "code": 10200, 432 | "message": "Authorization success!" 433 | } 434 | ] 435 | ``` 436 | 437 | ### 上传文件接口 438 | 439 | ```python 440 | import requests 441 | 442 | files = {"file": open("D:\\log.txt", "rb")} 443 | r = requests.post("http://127.0.0.1:5000/upload", files=files) 444 | result = r.json() 445 | print(result) 446 | 447 | ``` 448 | 449 | 返回结果: 450 | 451 | ```json 452 | { 453 | "code": 10200, 454 | "message": "upload success!" 455 | } 456 | ``` 457 | 458 | ### 下载文件接口 459 | 460 | ```python 461 | import requests 462 | 463 | r = requests.get("http://127.0.0.1:5000/download", stream=True) 464 | 465 | with open("./log.txt", "wb") as f: 466 | for chunk in r.iter_content(chunk_size=512): 467 | f.write(chunk) 468 | ``` 469 | 470 | 文件默认保存在当前脚本文件所在目录,文件名为`log.txt`。 471 | 472 | ### 同一个URL,根据方法实现不同功能 473 | 474 | * ```GET```请求,一般用作获取数据接口。 475 | 476 | ```python 477 | import requests 478 | 479 | r = requests.get("http://127.0.0.1:5000/phone/1") 480 | result = r.json() 481 | print(result) 482 | 483 | ``` 484 | 485 | 返回结果: 486 | 487 | ```json 488 | [ 489 | { 490 | "code": 10101, 491 | "message": "The phone id is empty" 492 | }, 493 | { 494 | "code": 10201, 495 | "message": "get success", 496 | "data": { 497 | "id": 1, 498 | "name": "小米手机", 499 | "price": 1999 500 | } 501 | } 502 | ] 503 | ``` 504 | 505 | * ```PUT```请求,一般用作更新数据接口。 506 | 507 | ```python 508 | import requests 509 | 510 | data = {"name": "华为手机", "price": "3999"} 511 | r = requests.put("http://127.0.0.1:5000/phone/1", data=data) 512 | result = r.json() 513 | print(result) 514 | 515 | ``` 516 | 517 | 返回结果: 518 | 519 | ```json 520 | [ 521 | { 522 | "code": 10102, 523 | "message": "The updated phone id is empty" 524 | }, 525 | { 526 | "code": 10202, 527 | "message": "update success", 528 | "data": { 529 | "id": 1, 530 | "name": "华为手机", 531 | "price": "3999" 532 | } 533 | } 534 | ] 535 | ``` 536 | 537 | * ```DELETE```请求, 一般用作删除数据接口。 538 | 539 | ```python 540 | import requests 541 | 542 | r = requests.delete("http://127.0.0.1:5000/phone/1") 543 | result = r.json() 544 | print(result) 545 | 546 | ``` 547 | 548 | 返回结果: 549 | 550 | ```json 551 | [ 552 | { 553 | "code": 10103, 554 | "message": "The deleted phone id is empty" 555 | }, 556 | { 557 | "code": 10203, 558 | "message": "delete success" 559 | } 560 | ] 561 | ``` 562 | 563 | ### 通过Session记录登录状态 564 | 565 | ```python 566 | import requests 567 | 568 | s = requests.Session() 569 | r = s.post("http://127.0.0.1:5000/user_login", data={"username": "jack", "password": "123"}) 570 | result = r.json() 571 | print(result) 572 | 573 | r2 = s.get("http://127.0.0.1:5000/user_data") 574 | result2 = r2.json() 575 | print(result2) 576 | ``` 577 | 578 | 返回结果: 579 | 580 | ```json 581 | [ 582 | { 583 | "code": 10200, 584 | "message": "login success" 585 | }, 586 | { 587 | "code": 10200, 588 | "message": "hello, jack" 589 | } 590 | ] 591 | ``` 592 | 593 | ### 依赖接口的调用 594 | 595 | 一个获取抽奖号码的接口,需要先得到抽奖活动id 和 抽奖用户id 596 | 597 | ```python 598 | import requests 599 | 600 | # 获取活动id 601 | r = requests.get("http://127.0.0.1:5000/get_activity") 602 | result = r.json() 603 | print(result) 604 | activity_id = result["data"]["id"] 605 | 606 | # 获取用户id 607 | r = requests.get("http://127.0.0.1:5000/get_user") 608 | result = r.json() 609 | print(result) 610 | user_id = result["data"]["id"] 611 | 612 | # 调用获取抽奖号码接口 613 | data = {"aid": activity_id, "uid": user_id} 614 | r = requests.post("http://127.0.0.1:5000/lucky_number", data=data) 615 | result = r.json() 616 | print(result) 617 | ```` 618 | 619 | 返回结果: 620 | 621 | ```json 622 | [ 623 | { 624 | "code": 10200, 625 | "data": { 626 | "id": 1, 627 | "name": "618抽奖活动" 628 | }, 629 | "message": "success" 630 | }, 631 | { 632 | "code": 10200, 633 | "data": { 634 | "id": 1, 635 | "name": "张三" 636 | }, 637 | "message": "success" 638 | }, 639 | { 640 | "code": 10200, 641 | "data": 80092, 642 | "message": "Lucky draw number" 643 | } 644 | ] 645 | ``` 646 | -------------------------------------------------------------------------------- /flask_api/api_server.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import json 4 | import os 5 | import time 6 | from html import escape 7 | from random import randint 8 | from time import sleep 9 | 10 | from Crypto.Cipher import AES # 请安装 Crypto 11 | from flask import Flask 12 | from flask import jsonify 13 | from flask import request 14 | from flask import send_from_directory 15 | from flask import session 16 | from werkzeug.utils import secure_filename 17 | 18 | BASE_PATH = os.path.dirname(os.path.abspath(__file__)) 19 | UPLOAD_FOLDER = BASE_PATH + '/uploads' 20 | ALLOWED_EXTENSIONS = ['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', "json"] 21 | 22 | app = Flask(__name__) 23 | app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER 24 | app.secret_key = 'please-generate-a-random-secret_key' 25 | 26 | 27 | # === 实现 httpbin 风格的接口 === 28 | 29 | @app.route('/get', methods=['GET']) 30 | def httpbin_get(): 31 | return jsonify({ 32 | "args": dict(request.args), # 查询参数 33 | "headers": dict(request.headers), # 请求头(去掉末尾空行) 34 | "method": request.method, 35 | "origin": request.remote_addr, # 客户端 IP 36 | "url": request.url 37 | }) 38 | 39 | 40 | @app.route('/post', methods=['POST']) 41 | def httpbin_post(): 42 | # 区分 form-data/x-www-form-urlencoded 和 JSON 43 | if request.is_json: 44 | json_data = request.get_json() 45 | else: 46 | json_data = None 47 | 48 | return jsonify({ 49 | "args": dict(request.args), 50 | "data": request.get_data(as_text=True), # 原始请求体字符串 51 | "form": dict(request.form), # 表单数据 52 | "json": json_data, # JSON 数据(如果存在) 53 | "headers": dict(request.headers), 54 | "method": request.method, 55 | "origin": request.remote_addr, 56 | "url": request.url 57 | }) 58 | 59 | 60 | @app.route('/put', methods=['PUT']) 61 | def httpbin_put(): 62 | if request.is_json: 63 | json_data = request.get_json() 64 | else: 65 | json_data = None 66 | 67 | return jsonify({ 68 | "args": dict(request.args), 69 | "data": request.get_data(as_text=True), 70 | "json": json_data, 71 | "headers": dict(request.headers), 72 | "method": request.method, 73 | "origin": request.remote_addr, 74 | "url": request.url 75 | }) 76 | 77 | 78 | @app.route('/delete', methods=['DELETE']) 79 | def httpbin_delete(): 80 | if request.is_json: 81 | json_data = request.get_json() 82 | else: 83 | json_data = None 84 | 85 | return jsonify({ 86 | "args": dict(request.args), 87 | "data": request.get_data(as_text=True), 88 | "json": json_data, 89 | "headers": dict(request.headers), 90 | "method": request.method, 91 | "origin": request.remote_addr, 92 | "url": request.url 93 | }) 94 | 95 | 96 | def response(code=None, message=None, data=None): 97 | """ 98 | 定义返回参数统一格式 99 | """ 100 | if data is None: 101 | data = [] 102 | if code is None: 103 | code = 10200 104 | if message is None: 105 | message = "success" 106 | sleep(2) 107 | return jsonify({"code": code, "message": message, "data": data}) 108 | 109 | 110 | class Number: 111 | n = 0 112 | 113 | 114 | # 最简单的json格式返回 115 | @app.route('/') 116 | def hello_world(): 117 | return response(message="Welcome to API testing") 118 | 119 | 120 | # 简单的计数器, 每次请求+1 121 | @app.route('/add_one') 122 | def add_one(): 123 | Number.n = Number.n + 1 124 | return response(data={"number": Number.n}) 125 | 126 | 127 | # 通过 URI 传参 128 | @app.route('/user/') 129 | def user(username): 130 | msg = "hello, {}".format(username) 131 | return response(message=msg) 132 | 133 | 134 | # 根据用户id返回数据 135 | @app.route('/id/', methods=["GET", "POST"]) 136 | def get_uid(uid): 137 | if request.method == "GET": 138 | user_info = {"id": 1, "name": "tom", "age": 22} 139 | if uid != 1: 140 | return response(10102, "user id null") 141 | else: 142 | return response(data=user_info) 143 | else: 144 | return response(10101, "request method error") 145 | 146 | 147 | # 一般的get传参方式, 148 | @app.route("/search/", methods=["GET", "POST"]) 149 | def get_search(): 150 | if request.method == "GET": 151 | search_word = request.args.get('q', '') 152 | if search_word == "selenium": 153 | data_list = ["selenium教程", "seleniumhq.org", "selenium环境安装"] 154 | else: 155 | data_list = [] 156 | return response(data=data_list) 157 | else: 158 | return response(10101, "request method error") 159 | 160 | 161 | # post请求, from-data/x-www-from-urlencode参数方式 162 | @app.route('/login', methods=["GET", "POST"]) 163 | def post_login(): 164 | if request.method == "POST": 165 | username = request.form.get('username') 166 | password = request.form.get('password') 167 | 168 | if username is None or password is None: 169 | return response(10102, "username or password is None") 170 | elif username == "" or password == "": 171 | return response(10103, "username or password is null") 172 | elif username == "admin" and password == "a123456": 173 | return response(10200, "login success") 174 | else: 175 | return response(10104, "username or password error") 176 | else: 177 | return response(10101, "request method error") 178 | 179 | 180 | # post请求,json参数方式 181 | @app.route('/add_user', methods=["GET", "POST"]) 182 | def post_add_user(): 183 | if request.method == "POST": 184 | try: 185 | data = json.loads(request.get_data()) 186 | except json.decoder.JSONDecodeError: 187 | return response(10105, "format error") 188 | 189 | try: 190 | name = data["name"] 191 | age = data["age"] 192 | height = data["height"] 193 | except KeyError: 194 | return response(10102, "key null") 195 | else: 196 | if name == "": 197 | return response(10103, "name null") 198 | elif name == "tom": 199 | return response(10104, "name exist") 200 | else: 201 | data = {"name": name, "age": age, "height": height} 202 | return response(10200, "add success", data) 203 | else: 204 | return response(10101, "request method error") 205 | 206 | 207 | # 获取 header 信息处理 208 | @app.route('/header', methods=['GET', 'POST']) 209 | def post_header(): 210 | if request.method == 'POST': 211 | token = request.headers.get("token") 212 | ct = request.headers.get("Content-Type") 213 | return response(10200, "header ok!", {"token": token, "Content-Type": ct}) 214 | else: 215 | return response(10101, "request method error") 216 | 217 | 218 | # Base Auth认证 ["Basic","YWRtaW46YWRtaW4xMjM="] 219 | @app.route('/auth', methods=['GET', 'POST']) 220 | def post_auth(): 221 | if request.method == 'POST': 222 | auth = request.headers.get("Authorization") 223 | if auth is None: 224 | return response(10101, "Authorization None") 225 | else: 226 | auth = auth.split() 227 | auth_parts = base64.b64decode(auth[1]).decode('utf-8').partition(':') 228 | userid, password = auth_parts[0], auth_parts[2] 229 | if userid == "" or password == "": 230 | return response(10102, "Authorization null") 231 | 232 | if userid == "admin" and password == "admin123": 233 | return response(10200, "Authorization success!") 234 | else: 235 | return response(10103, "Authorization fail!") 236 | else: 237 | return response(10101, "request method error") 238 | 239 | 240 | # 文件后缀名的判断 241 | def allowed_file(filename): 242 | return '.' in filename and \ 243 | filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS 244 | 245 | 246 | # 文件上传的接口 247 | @app.route('/upload', methods=['GET', 'POST']) 248 | def upload_file(): 249 | if request.method == 'POST': 250 | f = request.files['file'] 251 | if f and allowed_file(f.filename): 252 | filename = secure_filename(f.filename) 253 | f.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 254 | return response(10200, "upload success!") 255 | else: 256 | return response(10102, "file type error!") 257 | 258 | else: 259 | return response(10101, "request method error") 260 | 261 | 262 | # 文件下载接口 263 | @app.route("/download", methods=['GET']) 264 | def download_file(): 265 | if request.method == "GET": 266 | return send_from_directory("./uploads", filename="log.txt", as_attachment=True) 267 | 268 | else: 269 | return jsonify({"code": 10101, "message": "request method error"}) 270 | 271 | 272 | # 一个URL, 根据不同的方法做不同的处理 273 | @app.route('/phone/', methods=['GET', 'POST', "PUT", "DELETE"]) 274 | def more_used(pid): 275 | if request.method == 'GET': 276 | if pid == 1: 277 | phone_info = { 278 | "id": pid, 279 | "name": "小米手机", 280 | "price": 1999 281 | } 282 | return response(10201, "get success", phone_info) 283 | else: 284 | return response(10101, "The phone id is empty") 285 | 286 | elif request.method == "PUT": 287 | if pid == 1: 288 | name = request.form.get('name') 289 | price = request.form.get('price') 290 | phone_info = { 291 | "id": pid, 292 | "name": name, 293 | "price": price 294 | } 295 | return response(10202, "update success", phone_info) 296 | else: 297 | return response(10102, "The updated phone id is empty") 298 | 299 | elif request.method == "DELETE": 300 | if pid == 1: 301 | return response(10203, "delete success") 302 | else: 303 | return response(10103, "The deleted phone id is empty") 304 | 305 | return response(10104, "request method error") 306 | 307 | 308 | # 通过Session 记录登录状态 309 | @app.route("/user_login", methods=['POST']) 310 | def session_login(): 311 | username = request.form['username'] 312 | password = request.form['password'] 313 | if username != "" and password != "": 314 | session['username'] = username 315 | return response(10200, 'login success') 316 | else: 317 | return response(10200, 'login faile') 318 | 319 | 320 | @app.route("/user_data") 321 | def session_user_data(): 322 | if 'username' in session: 323 | username = format(escape(session['username'])) 324 | return response(10200, 'hello, {}'.format(username)) 325 | return response(10200, 'hello, stranger') 326 | 327 | 328 | # 接口的依赖 329 | @app.route('/get_activity', methods=["GET", "POST"]) 330 | def get_activity(): 331 | if request.method == "GET": 332 | activity_info = {"id": 1, "name": "618抽奖活动"} 333 | return response(10200, "success", activity_info) 334 | else: 335 | return response(10101, "request method error") 336 | 337 | 338 | @app.route('/get_user', methods=["GET", "POST"]) 339 | def get_user(): 340 | if request.method == "GET": 341 | user_info = {"id": 1, "name": "张三"} 342 | return response(10200, "success", user_info) 343 | else: 344 | return response(10101, "request method error") 345 | 346 | 347 | @app.route('/lucky_number', methods=["GET", "POST"]) 348 | def get_lucky_number(): 349 | if request.method == "POST": 350 | activity_id = request.form.get('aid') 351 | user_id = request.form.get('uid') 352 | 353 | if activity_id is None or user_id is None: 354 | return response(10102, "username or password is None") 355 | 356 | elif activity_id == "" or user_id == "": 357 | return response(10103, "username or password is null") 358 | 359 | if int(activity_id) != 1: 360 | return response(10104, "activity id exist") 361 | 362 | if int(user_id) != 1: 363 | return response(10105, "user id not exist") 364 | 365 | number = randint(10000, 99999) 366 | return response(10200, "Lucky draw number", number) 367 | 368 | else: 369 | return response(10101, "request method error") 370 | 371 | 372 | # 签名的接口 时间戳+ MD5加密 373 | @app.route('/sign/', methods=['GET', 'POST']) 374 | def post_sign(): 375 | if request.method == 'POST': 376 | client_time = request.form.get('time') # 客户端时间戳 377 | client_sign = request.form.get('sign') # 客户端签名 378 | 379 | if client_time is None or client_sign is None: 380 | return response(10102, "time or sign is None") 381 | 382 | elif client_time == "" or client_sign == "": 383 | return response(10103, "time or sign is null") 384 | 385 | else: 386 | # 服务器时间 387 | now_time = time.time() # 例:1466426831 388 | server_time = str(now_time).split('.')[0] 389 | # 获取时间差 390 | time_difference = int(server_time) - int(client_time) 391 | if time_difference >= 60: 392 | return response(10104, "timeout") 393 | 394 | # 签名检查 395 | md5 = hashlib.md5() 396 | sign_str = client_time + "&Guest-Bugmaster" + "a=1&b=2" 397 | sign_bytes_utf8 = sign_str.encode(encoding="utf-8") 398 | md5.update(sign_bytes_utf8) 399 | sever_sign = md5.hexdigest() 400 | if sever_sign != client_sign: 401 | return response(10105, "sign error!") 402 | else: 403 | return response(10200, "sign success!") 404 | 405 | else: 406 | return response(10101, "request method error") 407 | 408 | 409 | ############ AES加密的接口 ############# 410 | 411 | BS = 16 412 | 413 | 414 | def unpad(s): return s[0: - ord(s[-1])] 415 | 416 | 417 | def decryptBase64(src): 418 | return base64.urlsafe_b64decode(src) 419 | 420 | 421 | def decryptAES(src): 422 | """ 423 | 解析AES密文 424 | """ 425 | src = decryptBase64(src) 426 | key = b'W7v4D60fds2Cmk2U' 427 | iv = b"1172311105789011" 428 | cryptor = AES.new(key, AES.MODE_CBC, iv) 429 | text = cryptor.decrypt(src).decode() 430 | return unpad(text) 431 | 432 | 433 | @app.route('/aes/', methods=['GET', 'POST']) 434 | def post_aes(): 435 | if request.method == 'POST': 436 | data = request.form.get('data') # AES加密的数据 437 | 438 | if data is None or data == "": 439 | return response(10102, "data is None") 440 | 441 | # 解密 442 | decode = decryptAES(data) 443 | # 转化为字典 444 | dict_data = json.loads(decode) 445 | return response(10200, "success", dict_data) 446 | 447 | else: 448 | return response(10101, "request method error") 449 | 450 | 451 | ############ end ############# 452 | 453 | 454 | if __name__ == '__main__': 455 | app.debug = True 456 | app.run(port=5000) # 设置端口 457 | -------------------------------------------------------------------------------- /sys_monitoring/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CRM Dashboard | Hyper - Responsive Bootstrap 4 Admin Dashboard 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | system monitoring 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Search 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Found 17 results 76 | 77 | 78 | 79 | 80 | 81 | 82 | Analytics Report 83 | 84 | 85 | 86 | 87 | 88 | How can I help you? 89 | 90 | 91 | 92 | 93 | 94 | User profile settings 95 | 96 | 97 | 98 | 99 | Users 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Erwin Brown 108 | UI Designer 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | Jacob Deo 118 | Developer 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | Hyper 140 | Dashboard 141 | CRM 142 | 143 | 144 | CRM 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | memonry total 158 | 16384MB 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | memory percent 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | CPU 194 | 2核 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | Booked Revenue 212 | 213 | 256G 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 234 | 235 | 236 | 237 | 238 | Today 239 | 240 | Yesterday 241 | 242 | Last Week 243 | 244 | Last Month 245 | 246 | 247 | 248 | memory 249 | 250 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 267 | 268 | 269 | 270 | 271 | Today 272 | 273 | Yesterday 274 | 275 | Last Week 276 | 277 | Last Month 278 | 279 | 280 | 281 | CPU 282 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 507 | 508 | 509 | --------------------------------------------------------------------------------