@@ -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,13 +356,90 @@ 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
362382svr.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");
383+ // New structured form data API provides clear separation between text fields and files
384+
385+ // Access text fields (from form inputs without files)
386+ std::string username = req.form.get_field("username");
387+ std::string bio = req.form.get_field("bio");
388+
389+ // Access uploaded files
390+ if (req.form.has_file("avatar")) {
391+ const auto& file = req.form.get_file("avatar");
392+ std::cout << "Uploaded file: " << file.filename
393+ << " (" << file.content_type << ") - "
394+ << file.content.size() << " bytes" << std::endl;
395+
396+ // Save to disk
397+ std::ofstream ofs(file.filename, std::ios::binary);
398+ ofs << file.content;
399+ }
400+
401+ // Handle multiple values with same name
402+ auto tags = req.form.get_fields("tags"); // e.g., multiple checkboxes
403+ for (const auto& tag : tags) {
404+ std::cout << "Tag: " << tag << std::endl;
405+ }
406+
407+ auto documents = req.form.get_files("documents"); // multiple file upload
408+ for (const auto& doc : documents) {
409+ std::cout << "Document: " << doc.filename
410+ << " (" << doc.content.size() << " bytes)" << std::endl;
411+ }
412+
413+ // Check existence before accessing
414+ if (req.form.has_field("newsletter")) {
415+ std::cout << "Newsletter subscription: " << req.form.get_field("newsletter") << std::endl;
416+ }
417+
418+ // Get counts for validation
419+ if (req.form.get_field_count("tags") > 5) {
420+ res.status = StatusCode::BadRequest_400;
421+ res.set_content("Too many tags", "text/plain");
422+ return;
423+ }
424+
425+ // Summary
426+ std::cout << "Received " << req.form.fields.size() << " text fields and "
427+ << req.form.files.size() << " files" << std::endl;
428+
429+ res.set_content("Upload successful", "text/plain");
430+ });
431+ ```
432+
433+ #### Legacy API (still supported)
434+
435+ > ** Note** : The ` req.files ` field has been removed. Use ` req.form.files ` and ` req.form.fields ` instead.
436+ > For backward compatibility, ` req.get_file_value() ` , ` req.has_file() ` , and ` req.get_file_values() ` methods are still available and will delegate to the new ` req.form ` API.
437+
438+ ``` cpp
439+ svr.Post(" /multipart" , [&](const auto & req, auto & res) {
440+ // Legacy API - still works but delegates to req.form internally
441+ auto ret = req.has_file("name1"); // -> req.form.has_file("name1")
442+ const auto& file = req.get_file_value("name1"); // -> req.form.get_file("name1")
366443 // file.filename;
367444 // file.content_type;
368445 // file.content;
@@ -376,16 +453,29 @@ svr.Post("/content_receiver",
376453 [&](const Request &req, Response &res, const ContentReader &content_reader) {
377454 if (req.is_multipart_form_data()) {
378455 // NOTE: `content_reader` is blocking until every form data field is read
379- MultipartFormDataItems files;
456+ // This approach allows streaming processing of large files
457+ MultipartFormDataItems items;
380458 content_reader (
381- [&](const MultipartFormData &file ) {
382- files .push_back(file );
459+ [&](const MultipartFormData &item ) {
460+ items .push_back(item );
383461 return true;
384462 },
385463 [&](const char *data, size_t data_length) {
386- files .back().content.append(data, data_length);
464+ items .back().content.append(data, data_length);
387465 return true;
388466 });
467+
468+ // Process the received items
469+ for (const auto& item : items) {
470+ if (item.filename.empty()) {
471+ // Text field
472+ std::cout << "Field: " << item.name << " = " << item.content << std::endl;
473+ } else {
474+ // File
475+ std::cout << "File: " << item.name << " (" << item.filename << ") - "
476+ << item.content.size() << " bytes" << std::endl;
477+ }
478+ }
389479 } else {
390480 std::string body;
391481 content_reader ([ &] (const char * data, size_t data_length) {
0 commit comments