一、实验要求
想象你要做一个联网对战的卡牌游戏,你需要 用户注册/登录、用户获取个人信息、用户修改个人信息 等等。该作业就是实现以上几个功能
- 实现 输入用户名和密码进行注册、登录;注册、登录后应有信息回显 (显示成功/失败,和服务端返回的信息)
- 实现 获取用户信息
- (加分项) 实现 修改个人信息
修改个人信息只是修改“卡组”信息,输入框中直接输入“卡组”的json
这一功能需要通过cookie认证身份,即在登录后需要记录cookie (enableCookies)
使用enableCookies非常简单,因此需要在报告中体现 “思考enableCookies的作用” 才能加分
二、源码解析
LoginRegisterScene.cpp
//登录按钮回调函数
void LoginRegisterScene::loginButtonCallback(cocos2d::Ref * pSender) {
// Your code here
if (usernameInput->getString() == "" || passwordInput->getString() == "")
{
this->messageBox->setString("Login Failed!\n");
return;
}
HttpRequest* request = new HttpRequest();
request->setRequestType(HttpRequest::Type::POST);
request->setUrl("http://127.0.0.1:8000/auth");
request->setResponseCallback(CC_CALLBACK_2(LoginRegisterScene::loginHttpRequestCompleted, this));
std::string postdata = "{\"username\" : \"" + usernameInput->getString() + "\",\"password\" :\""+ passwordInput->getString() + "\"}";
const char* postdt = postdata.c_str();
request->setRequestData(postdt, strlen(postdt));
cocos2d::network::HttpClient::getInstance()->send(request);
cocos2d::network::HttpClient::getInstance()->enableCookies(nullptr);
request->release();
}
上面函数需要注意:要加上enableCookies
,在服务端,当程序需要为某个客户端的请求创建一个session的时候,服务 器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id(或者cookie),如果已包含一个session id(cookie)则说明以前已经为此客户端创建 过session,服务器就按照session id(cookie)把这个session检索出来使用(如果 检索不到,可能会新建一个),如果客户端请求不包含session id(cookie),则 为此客户端创建一个session并且生成一个与此session相关联的session id(cookie),session id(cookie)的值应该是一个既不会重复,又不容易被找到规律以仿 造的字符串,这个session id(cookie)将被在本次响应中返回给客户端保存。
//注册按钮回调函数
void LoginRegisterScene::registerButtonCallback(Ref * pSender) {
// Your code here
if (usernameInput->getString() == "" || passwordInput->getString() == "")
{
this->messageBox->setString("Register Failed!\n");
return;
}
HttpRequest* request = new HttpRequest();
request->setRequestType(HttpRequest::Type::POST);
request->setUrl("http://127.0.0.1:8000/users");
request->setResponseCallback(CC_CALLBACK_2(LoginRegisterScene::registerHttpRequestCompleted, this));
//std::string postdata = "{\"username\" : " + usernameInput->getString()+", \"password\" :"+ passwordInput->getString() +"}";
std::string postdata ="{\"username\":\"" + usernameInput->getString() + "\", \"password\":\"" + passwordInput->getString()+"\"}";
const char* postdt = postdata.c_str();
request->setRequestData(postdt, strlen(postdt));
cocos2d::network::HttpClient::getInstance()->send(request);
request->release();
}
这里需要注意:严格按照PPT上对服务端数据的说明,key值和value都要有引号,所以一定要注意std::string postdata ="{\"username\":\"" + usernameInput->getString() + "\", \"password\":\"" + passwordInput->getString()+"\"}";
这句,和注释掉的那句对比一下,那句value值少了引号,导致注册登录看似正常,最后查询时会出错,所以记得value值加引号。
//登录回显函数
void LoginRegisterScene::loginHttpRequestCompleted(HttpClient* sender, HttpResponse* response)
{
auto buffer = response->getResponseData();
rapidjson::Document doc;
doc.Parse(buffer->data(), buffer->size());
if (doc["status"] == true)
{
this->messageBox->setString("Login OK!");
}
else
{
this->messageBox->setString(std::string("Login Failed!\n") + doc["msg"].GetString());
}
return;
}
//注册回显函数
void LoginRegisterScene::registerHttpRequestCompleted(HttpClient* sender, HttpResponse* response)
{
auto buffer = response->getResponseData();
rapidjson::Document doc;
doc.Parse(buffer->data(), buffer->size());
if (doc["status"] == true)
{
this->messageBox->setString("Register OK!");
}
else
{
this->messageBox->setString(std::string("Register Failed!\n") + doc["msg"].GetString());
}
return;
}
ModifyUserScene.cpp
void ModifyUserScene::putDeckButtonCallback(Ref * pSender) {
// Your code here
HttpRequest* request = new HttpRequest();
request->setRequestType(HttpRequest::Type::PUT);
request->setUrl("http://127.0.0.1:8000/users");
request->setResponseCallback(CC_CALLBACK_2(ModifyUserScene::modifyHttpRequestCompleted, this));
std::string putdata = "{\"deck\":" + deckInput->getString()+"}";
const char* putdt = putdata.c_str();
request->setRequestData(putdt, strlen(putdt));
cocos2d::network::HttpClient::getInstance()->send(request);
request->release();
}
这里基本上没有难点,注意ppt上对put的json数据格式的要求就可以。
void ModifyUserScene::modifyHttpRequestCompleted(HttpClient* sender, HttpResponse* response)
{
auto buffer = response->getResponseData();
rapidjson::Document doc;
doc.Parse(buffer->data(), buffer->size());
if (doc["status"] == true)
{
this->messageBox->setString("PUT OK!");
}
else
{
this->messageBox->setString(std::string("PUT Failed!\n") + doc["msg"].GetString());
}
return;
}
UsersInfoScene.cpp
void UsersInfoScene::getUserButtonCallback(Ref * pSender) {
// Your code here
HttpRequest* request = new HttpRequest();
request->setRequestType(HttpRequest::Type::GET);
std::string url = "http://127.0.0.1:8000/users?limit=" + limitInput->getString();
request->setUrl(url);
request->setResponseCallback(CC_CALLBACK_2(UsersInfoScene::getUserHttpRequestCompleted, this));
cocos2d::network::HttpClient::getInstance()->send(request);
request->release();
}
void UsersInfoScene::getUserHttpRequestCompleted(HttpClient* sender, HttpResponse* response)
{
auto buffer = response->getResponseData();
std::string output = "";
rapidjson::Document doc;
doc.Parse(buffer->data(), buffer->size());
if (doc["status"] == true)
{//不同用户之间的循环
for (int i = 0; i <doc["data"].Size(); i++)
{ //一个用户下的循环
output = output + "Username : " + doc["data"][i]["username"].GetString() + "\n";
output += "Deck:\n";
for (int k = 0; k < (doc["data"])[i]["deck"].Size(); k++)
{
for (rapidjson::Value::ConstMemberIterator itr = ((doc["data"])[i]["deck"])[k].MemberBegin(); itr != ((doc["data"])[i]["deck"])[k].MemberEnd(); itr++)
{
rapidjson::Value jKey;
rapidjson::Value jValue;
Document::AllocatorType allocator;
jKey.CopyFrom(itr->name, allocator);
jValue.CopyFrom(itr->value, allocator);
if (jKey.IsString())
{
std::string name = jKey.GetString();
int value = jValue.GetInt();
std::stringstream stream;
stream << value;
std::string values = stream.str();
output = output+ " "+name+':' + values + '\n';
}
}
output += " _ _ _\n";
}
output += "_ _ _\n";
}
this->messageBox->setString(output);
}
else
{
this->messageBox->setString(std::string("Register Failed!\n") + doc["msg"].GetString());
}
return;
}
问题最多的就是这部分,首先要清楚服务端返回的数据格式是什么样的,仔细看PPT中的说明,怎么仔细都不为过。
然后外层循环可以直接用int类型++实现,但内层特别是获取key和value对的时候要注意,此时没有已知的key,所以要用迭代器来遍历整个数组,然后依次取得相应的key和value。取得value后不能直接输出,因为它是int类型直接加在string中输出会出现乱码,所以要将它转化为string类型然后加在string中输出。
三、经验总结
参考链接:
实验代码
对我来说,这次作业的难点在解析json数据上面,一开始直接用int++循环来处理,走不通,百度求助,看了很多文章介绍直接用*itr处理,行不通,最后看到链接中的那种方法解决了。还有就是要注意json返回出来的value的数据类型。好多次我直接在卡牌数目上用GetString()。导致错误。不同类型用不同函数GetInt(),GetArray()等。