@@ -99,28 +99,28 @@ auto res = cli.Get("/");
9999if (!res) {
100100 // Check the error type
101101 auto err = res.error();
102-
102+
103103 switch (err) {
104104 case httplib::Error::SSLConnection:
105- std::cout << "SSL connection failed, SSL error: "
105+ std::cout << "SSL connection failed, SSL error: "
106106 << res->ssl_error() << std::endl;
107107 break;
108108
109109 case httplib::Error::SSLLoadingCerts:
110- std::cout << "SSL cert loading failed, OpenSSL error: "
110+ std::cout << "SSL cert loading failed, OpenSSL error: "
111111 << std::hex << res->ssl_openssl_error() << std::endl;
112112 break;
113-
113+
114114 case httplib::Error::SSLServerVerification:
115- std::cout << "SSL verification failed, X509 error: "
115+ std::cout << "SSL verification failed, X509 error: "
116116 << res->ssl_openssl_error() << std::endl;
117117 break;
118-
118+
119119 case httplib::Error::SSLServerHostnameVerification:
120- std::cout << "SSL hostname verification failed, X509 error: "
120+ std::cout << "SSL hostname verification failed, X509 error: "
121121 << res->ssl_openssl_error() << std::endl;
122122 break;
123-
123+
124124 default:
125125 std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
126126 }
@@ -356,16 +356,80 @@ svr.set_pre_request_handler([](const auto& req, auto& res) {
356356});
357357```
358358
359- ### 'multipart/form-data' POST data
359+ ### Form data handling
360+
361+ #### URL-encoded form data ('application/x-www-form-urlencoded')
362+
363+ ``` cpp
364+ svr.Post(" /form" , [&](const auto & req, auto & res) {
365+ // URL query parameters and form-encoded data are accessible via req.params
366+ std::string username = req.get_param_value("username");
367+ std::string password = req.get_param_value("password");
368+
369+ // Handle multiple values with same name
370+ auto interests = req.get_param_values("interests");
371+
372+ // Check existence
373+ if (req.has_param("newsletter")) {
374+ // Handle newsletter subscription
375+ }
376+ });
377+ ```
378+
379+ #### 'multipart/form-data' POST data
360380
361381``` cpp
362- svr.Post(" /multipart" , [&](const auto & req, auto & res) {
363- auto size = req.files.size();
364- auto ret = req.has_file("name1");
365- const auto& file = req.get_file_value("name1");
366- // file.filename;
367- // file.content_type;
368- // file.content;
382+ svr.Post(" /multipart" , [&](const Request& req, Response& res) {
383+ // Access text fields (from form inputs without files)
384+ std::string username = req.form.get_field("username");
385+ std::string bio = req.form.get_field("bio");
386+
387+ // Access uploaded files
388+ if (req.form.has_file("avatar")) {
389+ const auto& file = req.form.get_file("avatar");
390+ std::cout << "Uploaded file: " << file.filename
391+ << " (" << file.content_type << ") - "
392+ << file.content.size() << " bytes" << std::endl;
393+
394+ // Access additional headers if needed
395+ for (const auto& header : file.headers) {
396+ std::cout << "Header: " << header.first << " = " << header.second << std::endl;
397+ }
398+
399+ // Save to disk
400+ std::ofstream ofs(file.filename, std::ios::binary);
401+ ofs << file.content;
402+ }
403+
404+ // Handle multiple values with same name
405+ auto tags = req.form.get_fields("tags"); // e.g., multiple checkboxes
406+ for (const auto& tag : tags) {
407+ std::cout << "Tag: " << tag << std::endl;
408+ }
409+
410+ auto documents = req.form.get_files("documents"); // multiple file upload
411+ for (const auto& doc : documents) {
412+ std::cout << "Document: " << doc.filename
413+ << " (" << doc.content.size() << " bytes)" << std::endl;
414+ }
415+
416+ // Check existence before accessing
417+ if (req.form.has_field("newsletter")) {
418+ std::cout << "Newsletter subscription: " << req.form.get_field("newsletter") << std::endl;
419+ }
420+
421+ // Get counts for validation
422+ if (req.form.get_field_count("tags") > 5) {
423+ res.status = StatusCode::BadRequest_400;
424+ res.set_content("Too many tags", "text/plain");
425+ return;
426+ }
427+
428+ // Summary
429+ std::cout << "Received " << req.form.fields.size() << " text fields and "
430+ << req.form.files.size() << " files" << std::endl;
431+
432+ res.set_content("Upload successful", "text/plain");
369433});
370434```
371435
@@ -376,16 +440,29 @@ svr.Post("/content_receiver",
376440 [&](const Request &req, Response &res, const ContentReader &content_reader) {
377441 if (req.is_multipart_form_data()) {
378442 // NOTE: `content_reader` is blocking until every form data field is read
379- MultipartFormDataItems files;
443+ // This approach allows streaming processing of large files
444+ std::vector<FormData> items;
380445 content_reader (
381- [&](const MultipartFormData &file ) {
382- files .push_back(file );
446+ [&](const FormData &item ) {
447+ items .push_back(item );
383448 return true;
384449 },
385450 [&](const char *data, size_t data_length) {
386- files .back().content.append(data, data_length);
451+ items .back().content.append(data, data_length);
387452 return true;
388453 });
454+
455+ // Process the received items
456+ for (const auto& item : items) {
457+ if (item.filename.empty()) {
458+ // Text field
459+ std::cout << "Field: " << item.name << " = " << item.content << std::endl;
460+ } else {
461+ // File
462+ std::cout << "File: " << item.name << " (" << item.filename << ") - "
463+ << item.content.size() << " bytes" << std::endl;
464+ }
465+ }
389466 } else {
390467 std::string body;
391468 content_reader ([ &] (const char * data, size_t data_length) {
@@ -691,7 +768,7 @@ auto res = cli.Post("/post", params);
691768### POST with Multipart Form Data
692769
693770``` c++
694- httplib::MultipartFormDataItems items = {
771+ httplib::UploadFormDataItems items = {
695772 { "text1", "text default", "", "" },
696773 { "text2", "aωb", "", "" },
697774 { "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },
0 commit comments