@@ -99,28 +99,28 @@ auto res = cli.Get("/");
99
99
if (!res) {
100
100
// Check the error type
101
101
auto err = res.error();
102
-
102
+
103
103
switch (err) {
104
104
case httplib::Error::SSLConnection:
105
- std::cout << "SSL connection failed, SSL error: "
105
+ std::cout << "SSL connection failed, SSL error: "
106
106
<< res->ssl_error() << std::endl;
107
107
break;
108
108
109
109
case httplib::Error::SSLLoadingCerts:
110
- std::cout << "SSL cert loading failed, OpenSSL error: "
110
+ std::cout << "SSL cert loading failed, OpenSSL error: "
111
111
<< std::hex << res->ssl_openssl_error() << std::endl;
112
112
break;
113
-
113
+
114
114
case httplib::Error::SSLServerVerification:
115
- std::cout << "SSL verification failed, X509 error: "
115
+ std::cout << "SSL verification failed, X509 error: "
116
116
<< res->ssl_openssl_error() << std::endl;
117
117
break;
118
-
118
+
119
119
case httplib::Error::SSLServerHostnameVerification:
120
- std::cout << "SSL hostname verification failed, X509 error: "
120
+ std::cout << "SSL hostname verification failed, X509 error: "
121
121
<< res->ssl_openssl_error() << std::endl;
122
122
break;
123
-
123
+
124
124
default:
125
125
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
126
126
}
@@ -356,16 +356,80 @@ svr.set_pre_request_handler([](const auto& req, auto& res) {
356
356
});
357
357
```
358
358
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
360
380
361
381
``` 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");
369
433
});
370
434
```
371
435
@@ -376,16 +440,29 @@ svr.Post("/content_receiver",
376
440
[&](const Request &req, Response &res, const ContentReader &content_reader) {
377
441
if (req.is_multipart_form_data()) {
378
442
// 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;
380
445
content_reader (
381
- [&](const MultipartFormData &file ) {
382
- files .push_back(file );
446
+ [&](const FormData &item ) {
447
+ items .push_back(item );
383
448
return true;
384
449
},
385
450
[&](const char *data, size_t data_length) {
386
- files .back().content.append(data, data_length);
451
+ items .back().content.append(data, data_length);
387
452
return true;
388
453
});
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
+ }
389
466
} else {
390
467
std::string body;
391
468
content_reader ([ &] (const char * data, size_t data_length) {
@@ -691,7 +768,7 @@ auto res = cli.Post("/post", params);
691
768
### POST with Multipart Form Data
692
769
693
770
``` c++
694
- httplib::MultipartFormDataItems items = {
771
+ httplib::UploadFormDataItems items = {
695
772
{ "text1", "text default", "", "" },
696
773
{ "text2", "aωb", "", "" },
697
774
{ "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },
0 commit comments