跳转至

参数输入 参考示例

在 IHttpCore 中的参数,可以有以下的写法

内置类型

在 IWebCore 种,有一系列的内置类型可以直接引用这些类型分别为:

IRequest

  • 表示整个引用
  • 写入参数必须是引用的形式,如果直接引用会报错
  • 可以是非常量引用,也可以是常量引用。
  • 常量引用将限制获取的内容仅为请求信息,为非修改的内容。
  • 非常量引用可以获取更多的信息。
  • 举例如下:
#pragma once

#include "http/controller/IHttpControllerInterface.h"

class ArgumentTypeController : public IHttpControllerInterface<ArgumentTypeController, true>
{
    Q_GADGET
    $AsController(ArgumentTypeController)
public:
    ArgumentTypeController();

    $GetMapping(emptyRequest)
    QString emptyRequest();

    $GetMapping(irequestRef)
    QString irequestRef(IRequest& request);

    $GetMapping(constIRequestRef)
    QString constIRequestRef(const IRequest& request);

//    $GetMapping(irequest) // this is not valid
    QString irequest(IRequest request);
};

IResponse

  • 表示响应的数据
  • 可引用,可不引用调用
  • 举例如下:
1
2
3
4
5
6
$GetMapping(iresponseRef)
QString iresponseRef(IResponse& response)
{
    response.setHeader("hello", "world");
    return "hello world";
}

IHttpCookieJar

  • 这个是对 cookie 信息的管理
  • 包括 请求中带入的cookie, 和响应给客户的cookie
  • 举例如下:
    // hello:world, qichu:begining
    $GetMapping(cookieJar)
    QString cookieJar(IHttpCookieJar& jar){
        QString ret;
        auto reqCookieKeys = jar.requestCookieKeys();
        ret.append(QString::fromStdString(reqCookieKeys.join(",")));
        if(jar.getRequestCookies("hello").size() == 0){
            qFatal("error");
        }

        auto reqCookies = jar.requestCookies();
        for(auto val : reqCookies.keys()){
            ret.append(val.toQString()).append(",").append(reqCookies.value(val).toQString());
        }

        auto cookie = jar.getRequestCookie("hello");
        if(cookie.m_value != "world"){
            return "error";
        }

        return ret;
    }
1
2
3
4
5
def test_cookieJar():
    url = serverAddress + "/cookie/cookieJar"
    response = requests.get(url, cookies={"hello":"world", "qichu":"begining"})
    assert response.status_code == 200
    print(response.text)

IHttpHeaderJar

  • 这个是对 header 的管理,包括 请求的header 和响应的header

  • 举例如下:

    $GetMapping(headerJar)
    QString headerJar(IHttpHeaderJar& jar){
        auto headers = jar.requestHeaderKeys();
        if(!jar.containRequestHeaderKey("hello")){
            qFatal("error");
        }

        jar.addResponseHeader("hello", "world");
        if(!jar.containResponseHeaderKey("hello")){
            qFatal("error");
        }
        jar.deleteReponseHeader("hello");
        if(jar.containResponseHeaderKey("hello")){
            qFatal("error");
        }

        auto& header = jar.responseHeaders();
        header.insert("j", "k");
        if(!jar.containResponseHeaderKey("j")){
            qFatal("error");
        }
        if(header.value("j") != "k"){
            qFatal("error");
        }
        header.remove("j");
        if(jar.containResponseHeaderKey("j")){
            qFatal("error");
        }
        if(jar.responseHeaderKeys().contains("j")){
           qFatal("error");
        }

        return "hello world";
    }
def test_headerJar():
    url = serverAddress + "/header/headerJar"
    headers = {
        "hello": "world",
        "abcd" : "efgh"
    }
    response = requests.get(url, headers=headers)
    assert response.status_code == 200
    assert response.headers.get("Server") == "IWebCore"
    assert response.headers.get("Content-Type") == "text/plain; charset=UTF-8"
    assert response.text == "hello world"

IHttpSessionJar

  • 这个是对 session 请求会话的管理。
  • IWerbCore 内置一个简单的 session 实现,默认使用这个实现。用户可以实现自己的session
  • 示例如下:
$GetMapping(session, /)
QString SessionArgument::session(IHttpSession &session)
{
    session.setValue("hello", "world");
    return "hello world";
}

$GetMapping(sessionValue)
QString SessionArgument::sessionValue(IHttpSession &session)
{
    return session.getValue("hello").toString();
}
def test_session():
    session = requests.Session()
    val = session.get(serverAddress + "/session")
    print(val.cookies)

    val2 = session.get(serverAddress + "/session/sessionValue")
    print(val2.text)
    val.cookies["ISessionId"] == val2.cookies["ISessionId"]
    print(val.cookies["ISessionId"])
    print(val2.cookies["ISessionId"])
    assert val2.text == "world"

IHttpMultiPartJar

  • 这个是对 multiPart 请求的合集。可以操作获取 multiPart 的所有内容
  • 示例如下:
1
2
3
4
    $PostMapping(multipartjar)
    QString multipartjar(const IHttpMultiPartJar& jar){
        return QString::fromStdString(jar.getNames().join(","));
    }
1
2
3
4
5
6
def test_multipartjar():
    val = requests.post(serverAddress + "/BasicArgument/multipartjar", data={"name": "test"}, files={"file": open("ServerConfig.py", "rb")}, verify= False)
    assert val.status_code == 200
    assert val.text == "name,file"
    print(val.text)
    print(val.status_code)

IHttpCookiePart

  • 这个是查询请求cookie 的内容
  • 示例如下:
1
2
3
4
    $GetMapping(cookiePart)
    QString cookiePart(IHttpCookiePart name){
        return name.m_value.toQString();
    }
1
2
3
4
5
6
def test_cookiePart():
    val = requests.get(serverAddress + "/BasicArgument/cookiePart", cookies={"name": "cookie"}, verify=False)
    assert val.status_code == 200
    assert val.text == "cookie"
    print(val.text)
    print(val.status_code)  

IHttpMultiPart

  • 这个是查找特定的 multiPart
  • 示例如下:
1
2
3
4
5
    $PostMapping(multipart)
    QString multipart(const IHttpMultiPart& name, const IHttpMultiPart& file){
        Q_UNUSED(name)
        return file.m_content.toQString();
    }
1
2
3
4
5
6
def test_mulitpart():
    val = requests.post(serverAddress + "/BasicArgument/multipart", data={"name": "test"}, files={"file": open("ServerConfig.py", "rb")}, verify= False)
    assert val.status_code == 200
    assert val.text == open("ServerConfig.py").read()
    print(val.text)
    print(val.status_code)

限定类型

$Path

  • 这个限定只允许参数从 path 参数中查找
  • 示例1
1
2
3
4
    $GetMapping(path, path/<path>)
    QString path(QString $Path(path)){
        return path;
    }
1
2
3
4
5
6
7
def test_path():
    url = serverAddress + "/restrictArg/path/hello"
    response = requests.get(url)
    assert response.status_code == 200
    assert len(response.text) != 0
    assert response.text == "hello"
    print(response.text)
  • 示例2
1
2
3
4
    $GetMapping(shortVal, /shortVal/<name>)
    QString shortVal(short $Path(name)){
        return QString::number(name);
    }
def test_shortVal():
    url = serverAddress + "/restrictPath/shortVal/123"
    response = requests.get(url)
    assert response.status_code == 200
    assert response.text == "123"
    print(response.text)

def test_shortVal_invalid():
    url = serverAddress + "/restrictPath/shortVal/invalid"
    response = requests.get(url)
    assert response.status_code == 400
    print(response.text)

def test_shortVal_negative():
    url = serverAddress + "/restrictPath/shortVal/-123"
    response = requests.get(url)
    assert response.status_code == 200
    print(response.text)
    assert response.text == "-123"

def test_shortVal_over_max():
    url = serverAddress + "/restrictPath/shortVal/32768"
    response = requests.get(url)
    assert response.status_code == 400
    print(response.text)

def test_shortVal_under_min():
    url = serverAddress + "/restrictPath/shortVal/-32769"
    response = requests.get(url)
    assert response.status_code == 400
    print(response.text)
  • 示例3
    $GetMapping(stringVal, /stringVal/<name>)
    QString stringVal(QString $Path(name)){
        return name;
    }

    $GetMapping(istringVal, /istringVal/<name>)
    QString istringVal(IString $Path(name)){
        return name.toQString();
    }

    $GetMapping(qstringVal, /qstringVal/<name>)
    QString qstringVal(QString $Path(name)){
        return name;
    }

    $GetMapping(dateVal, /dateVal/<name>)
    QString dateVal(QDate $Path(name)){
        return name.toString(Qt::DateFormat::ISODate);
    }

    $GetMapping(timeVal, /timeVal/<name>)
    QString timeVal(QTime $Path(name)){
        return name.toString(Qt::DateFormat::ISODate);
    }

    $GetMapping(dateTimeVal, /dateTimeVal/<name>)
    QString dateTimeVal(QDateTime $Path(name)){
        return name.toString(Qt::DateFormat::ISODate);
    }

    $GetMapping(byteVal, /byteVal/<name>)
    QString byteVal(QByteArray $Path(name)){
        return QString(name);
    }

$Query

  • 该限定只允许参数从 query 中查找

  • 示例:

1
2
3
4
    $GetMapping(query)
    QString query(QString $Query(query)){
        return query;
    }
1
2
3
4
5
6
7
def test_query():
    url = serverAddress + "/restrictArg/query?query=hello"
    response = requests.get(url)
    assert response.status_code == 200
    assert len(response.text) != 0
    assert response.text == "hello"
    print(response.text)

$OptQuery

  • 该参数允许query 不存在,或者在 query不存在的时候给定 一个默认值
1
2
3
4
5
6
7
8
9
    $GetMapping(optQuery)
    QString optQuery(QString $OptQuery(query)){
        return query;
    }

    $GetMapping(optQueryArg)
    QString optQueryArg(QString $OptQuery(query, hello)){
        return query;
    }
def test_optQuery():
    url = serverAddress + "/restrictArg/optQuery?query=hello"
    response = requests.get(url)
    assert response.status_code == 200
    assert len(response.text) != 0
    assert response.text == "hello"
    print(response.text)

def test_optQuery2():
    url = serverAddress + "/restrictArg/optQuery?query="
    response = requests.get(url)
    assert response.status_code == 200
    assert response.text == ""

def test_optQueryArg():
    url = serverAddress + "/restrictArg/optQueryArg?query=hello"
    response = requests.get(url)
    assert response.status_code == 200
    assert len(response.text) != 0
    assert response.text == "hello"
    print(response.text)

def test_optQueryArg2():
    url = serverAddress + "/restrictArg/optQueryArg"
    response = requests.get(url)
    assert response.status_code == 200
    assert response.text == "hello"

def test_optQueryArg3():
    url = serverAddress + "/restrictArg/optQueryArg?query="
    response = requests.get(url)
    assert response.status_code == 200
    assert response.text == ""
  • 该参数要求值从 请求中的header 中查找
$GetMapping(headerTypeInt)
QString headerTypeInt(int $Header(MyInt))
{
    return QString::number(MyInt);
}

$GetMapping(headerType)
QString headerType(IString $Header(MyHeader))
{
    return MyHeader.toQString();
}
def test_headerType():
    val = requests.get(serverAddress + "/BasicArgument/headerType", headers={"MyHeader": "Yuekeyuan"})
    print(val.text)
    print(val.status_code)  
    assert val.status_code == 200
    assert val.text == "Yuekeyuan"


def test_headerTypeInt():
    val = requests.get(serverAddress + "/BasicArgument/headerTypeInt", headers={"MyInt": "123"})
    print(val.text)
    print(val.status_code)  
    assert val.status_code == 200
    assert val.text == "123"

$OptHeader

  • header 值查找,可以为空,或者设置默认值
1
2
3
4
5
6
7
8
    $GetMapping(optHeader)
    QString optHeader(QString $OptHeader(header)){
        return header;
    }
    $GetMapping(optHeaderArg)
    QString optHeaderArg(QString $OptHeader(header, hello)){
        return header;
    }
def test_optHeader():
    url = serverAddress + "/restrictArg/optHeader"
    headers = {"header": "hello"}
    response = requests.get(url, headers=headers)
    assert response.status_code == 200
    assert len(response.text) != 0
    assert response.text == "hello"
    print(response.text)

def test_optHeader2():
    url = serverAddress + "/restrictArg/optHeader"
    response = requests.get(url)
    assert response.status_code == 200
    assert len(response.text) == 0
    assert response.text == ""

def test_optHeaderArg():
    url = serverAddress + "/restrictArg/optHeaderArg"
    headers = {"header": "hello"}
    response = requests.get(url, headers=headers)
    assert response.status_code == 200
    assert len(response.text) != 0
    assert response.text == "hello"
    print(response.text)

def test_optHeaderArg2():
    url = serverAddress + "/restrictArg/optHeaderArg"
    response = requests.get(url)
    assert response.status_code == 200
    assert len(response.text) == 5
    assert response.text == "hello"
  • 值从 cookie 中查找
  • 注意一点,cookie 一个 name 可以对应多个value, 所以这里支持 QStringList 转换
1
2
3
4
5
6
7
8
9
    $GetMapping(cookieString)
    QString cookieString(QString $Cookie(name)){
        return name;
    }

    $GetMapping(cookiesString)
    QString cookiesString(QStringList $Cookie(names)){
        return names.join(",");
    }
def test_cookieString():
    val = requests.get(serverAddress + "/BasicArgument/cookieString", cookies={"name": "cookie"}, verify=False)
    assert val.status_code == 200
    assert val.text == "cookie"
    print(val.text)
    print(val.status_code)

def test_cookiesStrings():
    session = requests.Session()

    # Set cookies in the session
    session.cookies.set('names', 'cookie_value1')
    session.cookies.set('names', 'cookie_value2')
    val = session.get(serverAddress + "/BasicArgument/cookiesString")
    assert val.status_code == 200
    # assert val.text == "cookie; name1=cookie1"
    print(val.text)
    print(val.status_code)

$OptCookie

  • 获取cookie, 可以为空,也可以默认值
1
2
3
4
5
6
7
8
    $GetMapping(optCookie)
    QString optCookie(QString $OptCookie(cookie)){
        return cookie;
    }
    $GetMapping(optCookieArg)
    QString optCookieArg(QString $OptCookie(cookie, hello)){
        return cookie;
    }
def test_optCookie():
    url = serverAddress + "/restrictArg/optCookie"
    response = requests.get(url, cookies={"cookie": "hello"})
    assert response.status_code == 200
    assert len(response.text) != 0
    assert response.text == "hello"
    print(response.text)

def test_optCookie2():
    url = serverAddress + "/restrictArg/optCookie"
    response = requests.get(url)
    assert response.status_code == 200
    assert len(response.text) == 0
    assert response.text == ""

def test_optCookieArg():
    url = serverAddress + "/restrictArg/optCookieArg"
    response = requests.get(url, cookies={"cookie": "hello"})
    assert response.status_code == 200
    assert response.text == "hello"
    print(response.text)

def test_optCookieArg2():
    url = serverAddress + "/restrictArg/optCookieArg"
    response = requests.get(url)
    assert response.status_code == 200
    assert response.text == "hello"

$Session

  • 从 session 中获取值,没有的话 返回404状态
  • TODO:

$OptSession

TODO:

$Json

  • 这个比较复杂
  • 先不解释了,以后可能还会改的
  • 具体的决策还没想好
1
2
3
4
5
6
7
8
9
    $PostMapping(boolVal, /boolVal) 
    QString boolVal(bool $Json(name)){
        return name ? "true" : "false";
    }

    $PostMapping(shortVal, /shortVal)
    QString shortVal(short $Json(name)){
        return QString::number(name);
    }
def test_boolVal():
    url = serverAddress + "/restrictJson/boolVal"
    response = requests.post(url, json={"name": True})
    assert response.status_code == 200
    assert response.text == "true"
    print(response.text)

def test_boolVal_False():
    url = serverAddress + "/restrictJson/boolVal"
    response = requests.post(url, json={"name": False})
    assert response.status_code == 200
    assert response.text == "false"
    print(response.text)

def test_boolVal_None():
    url = serverAddress + "/restrictJson/boolVal"
    response = requests.post(url, json={"name": None})
    assert response.status_code == 400

def test_boolVal_int():
    url = serverAddress + "/restrictJson/boolVal"
    response = requests.post(url, json={"name": 1})
    assert response.status_code == 200
    assert response.text == "true"
    print(response.text)
  • 嵌套
1
2
3
4
    $PostMapping(jsonPointerVal)
    QString jsonPointerVal(int $Json(name, value)){
        return QString::number(name);
    }
def test_jsonPointerVal():
    url = serverAddress + "/restrictJson/jsonPointerVal"
    response = requests.post(url, json={"name": {"value": 123}})
    assert response.status_code == 200
    assert response.text == "123"
    print(response.text)

    url = serverAddress + "/restrictJson/jsonPointerVal"
    response = requests.post(url, json={"name": {"value": "hello"}})
    assert response.status_code == 400
    print(response.text)

    url = serverAddress + "/restrictJson/jsonPointerVal"
    response = requests.post(url, json={"name": {"value": 123}, "age": {"value": 456}})
    assert response.status_code == 200
    assert response.text == "123"
    print(response.text)

    url = serverAddress + "/restrictJson/jsonPointerVal"
    response = requests.post(url, json={"name": {"value": "123"}, "age": {"value": "invalid"}})
    assert response.status_code == 400
    print(response.text)

$Body

  • 该标签会直接引用 body 的内容,并且将之转换成合适的类型
1
2
3
4
    $PostMapping(bodyType)
    QString bodyType(QString $Body(body)){
        return body;
    }
1
2
3
4
5
6
def test_BodyType():
    val = requests.post(serverAddress + "/BasicArgument/bodyType", data={"name": "test"})
    print(val.text)
    print(val.status_code)  
    assert val.status_code == 200
    assert val.text == "name=test"

直接类型

IJson

  • 这个类型和 $Json 限定类型不同的是,限定类型获取的是 json 中的内容,而 IJson 没有限定,获取的是完整的 json 内容。
// TODO:

其他类型

  • 数值类型,时间类型,字符串类型

  • 获取该值的顺序是

  • body 内容,包括 plainText, form data, json 和 multipart 内容

  • query 中的内容
  • path 中的内容
  • cookie 的内容
  • session 的内容
  • header 的内容

  • 所以,这个是一个危险的操作,因为如果你想获取 path 中的内容,而query 中有一个同名的内容,那么你获取的内容就和预期的不一样。

// TODO: demo

Bean 相关类型

IWebCore 支持 Bean 和复合类型

MyBean 定义

#pragma once

#include "core/bean/IBeanInterface.h"

class MyBean : public IBeanInterface<MyBean>
{
    Q_GADGET
public:
    MyBean() = default;
    MyBean(int id, QString name){
        this->index = id;
        this->name = name;
    }

    $BeanField(int, index, 100)
    $BeanField(QString, name, "yuekeyuan")
};

Bean

1
2
3
4
    $PostMapping(beanData)
    std::string beanData(MyBean bean){
        return bean.toJson().dump();
    }
def test_beanData():
    val = requests.post(serverAddress + "/BasicArgument/beanData", json={"index": 102, "name": "yueqichu"})
    print(val.text)
    print(val.status_code)
    assert val.text == '{"index":102,"name":"yueqichu"}'
    assert val.status_code == 200

def test_beanData2():
    val = requests.post(serverAddress + "/BasicArgument/beanData", json=[{"index": 102, "name": "yueqichu"},{"index": 103, "name": "yuekeyuan"}])
    print(val.text)
    print(val.status_code)
    # assert val.text == '{"index":102,"name":"yueqichu"}'
    assert val.status_code == 500

List\

QList\

  • 示例如下:
1
2
3
4
    $PostMapping(beanDatas)
    std::string beanDatas(QList<MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }
1
2
3
4
5
6
def test_beanDataQList():
    val = requests.post(serverAddress + "/BasicArgument/beanDatas", json=[{"index": 102, "name": "yueqichu"},{"index": 103, "name": "yuekeyuan"}])
    print(val.text)
    print(val.status_code)
    assert val.json() == [{"index": 102, "name": "yueqichu"}, {"index": 103, "name": "yuekeyuan"}]
    assert val.status_code == 200

std::list\

1
2
3
4
    $PostMapping(beanDataStdList)
    std::string beanDataStdList(std::list<MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }
1
2
3
4
5
6
def test_beanDataStdList():
    val = requests.post(serverAddress + "/BasicArgument/beanDataStdList", json=[{"index": 102, "name": "yueqichu"},{"index": 103, "name": "yuekeyuan"}])
    print(val.text)
    print(val.status_code)
    assert val.json() == [{"index": 102, "name": "yueqichu"}, {"index": 103, "name": "yuekeyuan"}]
    assert val.status_code == 200

QVector\

// TODO:

std::vector\

// TODO:

Map

QMap\

1
2
3
4
    $PostMapping(beanDataQMapStdString)
    std::string beanDataQMapStdString(QMap<std::string, MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }
1
2
3
4
5
6
7
def test_beanDataQMapStdString():
    val = requests.post(serverAddress + "/BasicArgument/beanDataQMapStdString", json={
        "fei" :{"index": 102, "name": "yueqichu"}, "yue": {"index": 103, "name": "yuekeyuan"}})
    print(val.text)
    print(val.status_code)
    assert val.json() == {"fei":{"index":102,"name":"yueqichu"},"yue":{"index":103,"name":"yuekeyuan"}}
    assert val.status_code == 200

QMap\

1
2
3
4
    $PostMapping(beanDataQMap)
    std::string beanDataQMap(QMap<IString, MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }

```python' def test_beanDataQMap(): val = requests.post(serverAddress + "/BasicArgument/beanDataQMap", json={ "fei" :{"index": 102, "name": "yueqichu"}, "yue": {"index": 103, "name": "yuekeyuan"}}) print(val.text) print(val.status_code) assert val.json() == {"fei":{"index":102,"name":"yueqichu"},"yue":{"index":103,"name":"yuekeyuan"}} assert val.status_code == 200

1
2
3
4
5
6
7
#### QMap<QString, Bean>

```cpp
    $PostMapping(beanDataQMapQString)
    std::string beanDataQMapQString(QMap<QString, MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }
1
2
3
4
5
6
7
def test_beanDataQMapQString():
    val = requests.post(serverAddress + "/BasicArgument/beanDataQMapQString", json={
        "fei" :{"index": 102, "name": "yueqichu"}, "yue": {"index": 103, "name": "yuekeyuan"}})
    print(val.text)
    print(val.status_code)
    assert val.json() == {"fei":{"index":102,"name":"yueqichu"},"yue":{"index":103,"name":"yuekeyuan"}}
    assert val.status_code == 200

std::map\

1
2
3
4
    $PostMapping(beanDataStdMapQString)
    std::string beanDataStdMapQString(std::map<QString, MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }
1
2
3
4
5
6
7
def test_beanDataStdMapQString():
    val = requests.post(serverAddress + "/BasicArgument/beanDataStdMapQString", json={
        "fei" :{"index": 102, "name": "yueqichu"}, "yue": {"index": 103, "name": "yuekeyuan"}})
    print(val.text)
    print(val.status_code)
    assert val.json() == {"fei":{"index":102,"name":"yueqichu"},"yue":{"index":103,"name":"yuekeyuan"}}
    assert val.status_code == 200

std::map\

1
2
3
4
    $PostMapping(beanDataStdMap)
    std::string beanDataStdMap(std::map<IString, MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }
1
2
3
4
5
6
7
def test_beanDataStdMap():
    val = requests.post(serverAddress + "/BasicArgument/beanDataStdMap", json={
        "fei" :{"index": 102, "name": "yueqichu"}, "yue": {"index": 103, "name": "yuekeyuan"}})
    print(val.text)
    print(val.status_code)
    assert val.json() == {"fei":{"index":102,"name":"yueqichu"},"yue":{"index":103,"name":"yuekeyuan"}}
    assert val.status_code == 200

std::map\

1
2
3
4
    $PostMapping(beanDataStdMapStdString)
    std::string beanDataStdMapStdString(std::map<std::string, MyBean> beans){
        return IJsonUtil::toJson(beans).dump();
    }
1
2
3
4
5
6
7
def test_beanDataStdMapStdString():
    val = requests.post(serverAddress + "/BasicArgument/beanDataStdMapStdString", json={
        "fei" :{"index": 102, "name": "yueqichu"}, "yue": {"index": 103, "name": "yuekeyuan"}})
    print(val.text)
    print(val.status_code)
    assert val.json() == {"fei":{"index":102,"name":"yueqichu"},"yue":{"index":103,"name":"yuekeyuan"}}
    assert val.status_code == 200

复合类型

复合类型, 比如 std::list\ 这种类型, 是由 基础类型 和 container 联合构造而成的。

// TODO