Skip to content

Commit af73377

Browse files
authored
Fix #1578 (#2171)
* Fix #1578 * Update README * Update * Update * Update * Update * Update * Update
1 parent a3f5569 commit af73377

File tree

5 files changed

+624
-469
lines changed

5 files changed

+624
-469
lines changed

README.md

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -99,28 +99,28 @@ auto res = cli.Get("/");
9999
if (!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" },

example/simplesvr.cc

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,26 @@ string dump_headers(const Headers &headers) {
2727
return s;
2828
}
2929

30-
string dump_multipart_files(const MultipartFormDataMap &files) {
30+
string dump_multipart_formdata(const MultipartFormData &form) {
3131
string s;
3232
char buf[BUFSIZ];
3333

3434
s += "--------------------------------\n";
3535

36-
for (const auto &x : files) {
36+
for (const auto &x : form.fields) {
37+
const auto &name = x.first;
38+
const auto &field = x.second;
39+
40+
snprintf(buf, sizeof(buf), "name: %s\n", name.c_str());
41+
s += buf;
42+
43+
snprintf(buf, sizeof(buf), "text length: %zu\n", field.content.size());
44+
s += buf;
45+
46+
s += "----------------\n";
47+
}
48+
49+
for (const auto &x : form.files) {
3750
const auto &name = x.first;
3851
const auto &file = x.second;
3952

@@ -77,7 +90,7 @@ string log(const Request &req, const Response &res) {
7790
s += buf;
7891

7992
s += dump_headers(req.headers);
80-
s += dump_multipart_files(req.files);
93+
s += dump_multipart_formdata(req.form);
8194

8295
s += "--------------------------------\n";
8396

@@ -101,7 +114,7 @@ int main(int argc, const char **argv) {
101114
#endif
102115

103116
svr.Post("/multipart", [](const Request &req, Response &res) {
104-
auto body = dump_headers(req.headers) + dump_multipart_files(req.files);
117+
auto body = dump_headers(req.headers) + dump_multipart_formdata(req.form);
105118

106119
res.set_content(body, "text/plain");
107120
});

example/upload.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ int main(void) {
3737
});
3838

3939
svr.Post("/post", [](const Request &req, Response &res) {
40-
auto image_file = req.get_file_value("image_file");
41-
auto text_file = req.get_file_value("text_file");
40+
const auto &image_file = req.form.get_file("image_file");
41+
const auto &text_file = req.form.get_file("text_file");
4242

4343
cout << "image file length: " << image_file.content.length() << endl
4444
<< "image file name: " << image_file.filename << endl

0 commit comments

Comments
 (0)