提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
上面一篇文章教你学会了Poco开发服务端应用,这个教程教会你使用JSON。一般传JSON的时候都是POST请求,很少有人把JSON序列化放在GET请求里,不安全。
如果你不会开发服务端就先看下下面的文章,通俗易懂。我直接开门见山了,默认环境已经有了。
一、JSON是什么?
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,设计上易于人类阅读和编写,同时也便于机器解析和生成。它源自 JavaScript 编程语言,但现在被广泛用于不同的编程环境中。
JSON 的特点
文本格式:JSON 是一种纯文本格式,由键值对和数组组成。
语言无关:虽然最初基于 JavaScript,但现在几乎所有编程语言都支持 JSON。
轻量级:结构简单,适用于数据交换,尤其是在 Web 应用程序中。
二、使用步骤
基于上面的代码在RequestHander里面先解析JSON,再写回JSON。
UpdateRequestHanler.cpp
//
// Created by anold on 2024-08-06.
//
#ifndef POCO_HTTP_SERVER_UPDATEREQUESTHANDLER_H
#define POCO_HTTP_SERVER_UPDATEREQUESTHANDLER_H
#include <iostream>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/JSON/Parser.h>
#include <Poco/JSON/Stringifier.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/Data/RecordSet.h>
#include <Poco/Data/DataException.h>
#include <Poco/Data/MySQL/MySQLException.h>
using namespace Poco::Net;
class UpdateRequestHandler : public Poco::Net::HTTPRequestHandler {
public:
void handleRequest(HTTPServerRequest &request, HTTPServerResponse &response) override {
response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
response.setContentType("application/json");
auto &os = response.send();
//root就是一会要response的JSON结构,因为就算出错了也要告诉请求人结果
Poco::JSON::Object root;
//JSON对象转换成字符串流
std::stringstream ss;
try {
//JSON解析代码,可以直接把request.stream()传进去一次性解析出来
Poco::JSON::Parser parser;
Poco::Dynamic::Var result = parser.parse(request.stream());
//object 就是最终解析的JSON对象,是个智能指针不需要手动回收内存,如果解析失败会触发异常
Poco::JSON::Object::Ptr object = result.extract<Poco::JSON::Object::Ptr>();
//JSON取值代码,这里没有对key是否存在和类型进行判断,二十借助了异常简化了代码实现
//比如需要int类型,哪怕传个数字字符串也可以正常解析,会自动转换,如果是字母就抛异常
auto id = object->getValue<int>("id");
auto class_id = object->getValue<int>("class_id");
//数据库会话池
Session session(MySQLSessionPool::pool->get());
if (!session.isGood()) {
root.set("status", -400);
root.set("message", "Error Connect To MySQL");
//JSON对象写入字符串流然后序列化成字符串response
Poco::JSON::Stringifier::stringify(root, ss);
os << ss.str();
return;
}
//语句执行
Statement statement(session);
statement << "UPDATE student SET class_id=? WHERE id=?", use(class_id), use(id);
auto rows = statement.execute();
std::cout << "Effect Rows: " << rows << std::endl;
if (statement.done() && rows == 1) {
root.set("status", 200);
root.set("message", "Success");
Poco::JSON::Stringifier::stringify(root, ss);
os << ss.str();
} else {
root.set("status", -500);
root.set("message", "Error Execute Statement");
Poco::JSON::Stringifier::stringify(root, ss);
os << ss.str();
return;
}
} catch (const Poco::Data::MySQL::MySQLException &e) {
std::cout << "MySQL Exception: " << e.message() << std::endl;
root.set("status", -600);
root.set("message", "MySQL Exception: " + e.message());
Poco::JSON::Stringifier::stringify(root, ss);
os << ss.str();
} catch (const Poco::Exception &e) {
std::cout << "Poco Exception: " << e.message() << std::endl;
root.set("status", -200);
root.set("message", "Poco Exception: " + e.message());
Poco::JSON::Stringifier::stringify(root, ss);
os << ss.str();
} catch (const std::exception &e) {
std::cout << "Std Exception: " << e.what() << std::endl;
root.set("status", -300);
root.set("message", "Std Exception: " + std::string(e.what()));
Poco::JSON::Stringifier::stringify(root, ss);
os << ss.str();
}
}
};
#endif //POCO_HTTP_SERVER_UPDATEREQUESTHANDLER_H
注:为什么用3个异常处理?
1、如果JSON
格式出错理论上你需要对每个传进来的JSON值进行两步校验,即是否存在这个key
和value
是否是你需要的类型,否则一旦出错就会出现崩溃的风险,不要抱侥幸心理认为只要传值的人不犯错就没事,你是服务者,你要做的就是一定不能出错,至少不能崩溃,最差的情况哪怕HTTP请求超时也不是不能接受。
2、本身Poco框架就刻意简化了处理方式,比如Poco::Exception
可以捕捉所有的JSON错误(不止JSON,可以看下源码类层次)
,哪怕传过来的是空字符串
, e.message()
可以获取具体的错误信息,将这个信息反回即可。哪怕你默认的请求是POST
,别人给你传了GET
,这段代码也是可以正常运行的,它会报错,但是美中不足的是报错的原因无法看出是Method
的问题,这一点我觉得无关紧要,这肯定是请求者的问题,我不可能考虑的面面俱到。同样,Poco框架虽然提供了获取Method的方法,但是它没有过度区分到底是POST还是GET,而是把它归到了一起由开发者来决定处理方法,况且区分Method
也是对请求人的硬性要求。
3、Poco::Data::MySQL::MySQLException
捕捉数据库相关异常,但是注意:捕捉不了所有的CIUD
结果,有些结果不触发异常,比如UPDATE
语句,statement << "UPDATE student SET class_id=? WHERE id=?", use(class_id), use(id);
这句的执行结果可能不成功,但是它可能不抛异常,可以通过返回影响行数
来判断语句是否成功。std::exception
能捕捉任何运行时异常,是最后的保命手段。不要担心异常会影响性能,异常对性能最大的影响是触发时的堆栈展开(stack unwinding)
,但是堆栈展开
只有异常发生的时候才会触发,很多编译器
对异常有优化,没有触发异常的语句可以获得近乎原生的性能,所以大胆地使用异常吧,但不要滥用。
总结
1、最后的总结才是最核心的部分,特别适合初学者,能少走很多弯路。