使用 C# 和 Flurl 的多部分 POST 到 Joplin REST API
Multipart POST to the Joplin REST API using C# and Flurl
我目前正在开发一个控制台应用程序,使用 C# 和 Flurl 将数据导入 Joplin for Windows 10。
可在 here.
中找到 Joplin 的 API 描述
我正尝试在 Joplin 中为我系统上的文件创建一个新资源,以便它可以附加到 Joplin 笔记中。
使用 CURL,我可以使用以下命令创建资源:
curl -F "data=@c:\temp\Test.pptx" -F "props={\"title\":\"my resource title\"}" http://localhost:41184/resources?token=MyToken
(注意:它仅适用于 "data=@c:\temp\Test.pptx",不适用于 "data=c:\temp\Test.pptx")
当我在 C# 中使用 Flurl 尝试此操作时,我从 Joplin 收到了 400 响应,在日志中我发现:
Error: Resource cannot be created without a file
at Api.action_resources (C:\Program Files\Joplin\resources\app.asar\lib\services\rest\Api.js:351:37)
at Api.route (C:\Program Files\Joplin\resources\app.asar\lib\services\rest\Api.js:140:42)
at execRequest (C:\Program Files\Joplin\resources\app.asar\lib\ClipperServer.js:157:39)
at C:\Program Files\Joplin\resources\app.asar\lib\ClipperServer.js:185:8
at C:\Program Files\Joplin\resources\app.asar\node_modules\multiparty\index.js:136:9
at C:\Program Files\Joplin\resources\app.asar\node_modules\multiparty\index.js:115:9
at processTicksAndRejections (internal/process/task_queues.js:75:11)"
到目前为止我已经试过了:
try
{
var url = BaseUrl
.WithHeader("User_Agent", browserUserAgent)
.AppendPathSegment("resources")
.SetQueryParam("token", Token);
using (var fs = new FileStream("c:\temp\Test.pptx", FileMode.Open, FileAccess.Read))
{
var resource = url.PostMultipartAsync(mp => mp
.AddJson("props", new { title = "test title" })
.AddFile("data", fs, "Test.pptx", "application/octet-stream")
)
.ReceiveJson<JoplinResource>()
.Result;
}
}
和:
try
{
var url = BaseUrl
.WithHeader("User_Agent", browserUserAgent)
.AppendPathSegment("resources")
.SetQueryParam("token", Token);
var resource = url.PostMultipartAsync(mp => mp
.AddJson("props", new { title = "test title" })
.AddFile("data", "c:\temp\Test.pptx")
)
.ReceiveJson<JoplinResource>()
.Result;
}
我连接了 fiddler 以查看我的应用程序和 CURL 之间的区别。
卷曲:
POST http://127.0.0.1:41184/resources?token=MyToken HTTP/1.1
Host: 127.0.0.1:41184
User-Agent: curl/7.70.0
Accept: */*
Connection: Keep-Alive
Content-Length: 33648
Content-Type: multipart/form-data; boundary=------------------------91ab181cbb0247ba
--------------------------91ab181cbb0247ba
Content-Disposition: form-data; name="props"
{"title":"my resource title"}
--------------------------91ab181cbb0247ba
Content-Disposition: form-data; name="data"; filename="Test.pptx"
Content-Type: application/octet-stream
...
我的控制台应用程序:
POST http://localhost:41184/resources?token=MyToken HTTP/1.1
User_Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36
Content-Type: multipart/form-data; boundary="f603841b-5c32-4e77-985a-69c2ffb6eed0"
Host: localhost:41184
Content-Length: 33612
Expect: 100-continue
Accept-Encoding: gzip, deflate
--f603841b-5c32-4e77-985a-69c2ffb6eed0
Content-Disposition: form-data; name=props
{"title":"My Resource"}
--f603841b-5c32-4e77-985a-69c2ffb6eed0
Content-Disposition: form-data; name=data; filename=Test.pptx; filename*=utf-8''Test.pptx
...
注意差异:
- props 和数据在使用 CURL 时用引号引起来,而不是在 FLURL 中
- FLURL 发送第二个文件名:
filename*=utf-8''Test.pptx
如何让它正常工作?
问题出在 "data" 和 "props":
的引号缺失
try
{
var url = BaseUrl
.WithHeader("User_Agent", browserUserAgent)
.AppendPathSegment("resources")
.SetQueryParam("token", Token);
var resource = url.PostMultipartAsync(mp => mp
.AddJson("\"props\"", new { title = "My Resource" })
.AddFile("\"data\"", "c:\temp\Test.pptx")
)
.ReceiveJson<JoplinResource>()
.Result;
}
原始请求 header 现在是:
POST http://localhost:41184/resources?token=MyToken HTTP/1.1
User_Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36
Content-Type: multipart/form-data; boundary="c6b2377a-1240-4ae3-872f-fa24b643d3e0"
Host: localhost:41184
Content-Length: 33616
Expect: 100-continue
Accept-Encoding: gzip, deflate
--c6b2377a-1240-4ae3-872f-fa24b643d3e0
Content-Disposition: form-data; name="props"
{"title":"My Resource"}
--c6b2377a-1240-4ae3-872f-fa24b643d3e0
Content-Disposition: form-data; name="data"; filename=Test.pptx; filename*=utf-8''Test.pptx
...
并且 Joplin REST 服务创建了一个新资源...
我目前正在开发一个控制台应用程序,使用 C# 和 Flurl 将数据导入 Joplin for Windows 10。 可在 here.
中找到 Joplin 的 API 描述我正尝试在 Joplin 中为我系统上的文件创建一个新资源,以便它可以附加到 Joplin 笔记中。
使用 CURL,我可以使用以下命令创建资源:
curl -F "data=@c:\temp\Test.pptx" -F "props={\"title\":\"my resource title\"}" http://localhost:41184/resources?token=MyToken
(注意:它仅适用于 "data=@c:\temp\Test.pptx",不适用于 "data=c:\temp\Test.pptx")
当我在 C# 中使用 Flurl 尝试此操作时,我从 Joplin 收到了 400 响应,在日志中我发现:
Error: Resource cannot be created without a file at Api.action_resources (C:\Program Files\Joplin\resources\app.asar\lib\services\rest\Api.js:351:37) at Api.route (C:\Program Files\Joplin\resources\app.asar\lib\services\rest\Api.js:140:42) at execRequest (C:\Program Files\Joplin\resources\app.asar\lib\ClipperServer.js:157:39) at C:\Program Files\Joplin\resources\app.asar\lib\ClipperServer.js:185:8 at C:\Program Files\Joplin\resources\app.asar\node_modules\multiparty\index.js:136:9 at C:\Program Files\Joplin\resources\app.asar\node_modules\multiparty\index.js:115:9 at processTicksAndRejections (internal/process/task_queues.js:75:11)"
到目前为止我已经试过了:
try
{
var url = BaseUrl
.WithHeader("User_Agent", browserUserAgent)
.AppendPathSegment("resources")
.SetQueryParam("token", Token);
using (var fs = new FileStream("c:\temp\Test.pptx", FileMode.Open, FileAccess.Read))
{
var resource = url.PostMultipartAsync(mp => mp
.AddJson("props", new { title = "test title" })
.AddFile("data", fs, "Test.pptx", "application/octet-stream")
)
.ReceiveJson<JoplinResource>()
.Result;
}
}
和:
try
{
var url = BaseUrl
.WithHeader("User_Agent", browserUserAgent)
.AppendPathSegment("resources")
.SetQueryParam("token", Token);
var resource = url.PostMultipartAsync(mp => mp
.AddJson("props", new { title = "test title" })
.AddFile("data", "c:\temp\Test.pptx")
)
.ReceiveJson<JoplinResource>()
.Result;
}
我连接了 fiddler 以查看我的应用程序和 CURL 之间的区别。
卷曲:
POST http://127.0.0.1:41184/resources?token=MyToken HTTP/1.1
Host: 127.0.0.1:41184
User-Agent: curl/7.70.0
Accept: */*
Connection: Keep-Alive
Content-Length: 33648
Content-Type: multipart/form-data; boundary=------------------------91ab181cbb0247ba
--------------------------91ab181cbb0247ba
Content-Disposition: form-data; name="props"
{"title":"my resource title"}
--------------------------91ab181cbb0247ba
Content-Disposition: form-data; name="data"; filename="Test.pptx"
Content-Type: application/octet-stream
...
我的控制台应用程序:
POST http://localhost:41184/resources?token=MyToken HTTP/1.1
User_Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36
Content-Type: multipart/form-data; boundary="f603841b-5c32-4e77-985a-69c2ffb6eed0"
Host: localhost:41184
Content-Length: 33612
Expect: 100-continue
Accept-Encoding: gzip, deflate
--f603841b-5c32-4e77-985a-69c2ffb6eed0
Content-Disposition: form-data; name=props
{"title":"My Resource"}
--f603841b-5c32-4e77-985a-69c2ffb6eed0
Content-Disposition: form-data; name=data; filename=Test.pptx; filename*=utf-8''Test.pptx
...
注意差异:
- props 和数据在使用 CURL 时用引号引起来,而不是在 FLURL 中
- FLURL 发送第二个文件名:
filename*=utf-8''Test.pptx
如何让它正常工作?
问题出在 "data" 和 "props":
的引号缺失 try
{
var url = BaseUrl
.WithHeader("User_Agent", browserUserAgent)
.AppendPathSegment("resources")
.SetQueryParam("token", Token);
var resource = url.PostMultipartAsync(mp => mp
.AddJson("\"props\"", new { title = "My Resource" })
.AddFile("\"data\"", "c:\temp\Test.pptx")
)
.ReceiveJson<JoplinResource>()
.Result;
}
原始请求 header 现在是:
POST http://localhost:41184/resources?token=MyToken HTTP/1.1
User_Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36
Content-Type: multipart/form-data; boundary="c6b2377a-1240-4ae3-872f-fa24b643d3e0"
Host: localhost:41184
Content-Length: 33616
Expect: 100-continue
Accept-Encoding: gzip, deflate
--c6b2377a-1240-4ae3-872f-fa24b643d3e0
Content-Disposition: form-data; name="props"
{"title":"My Resource"}
--c6b2377a-1240-4ae3-872f-fa24b643d3e0
Content-Disposition: form-data; name="data"; filename=Test.pptx; filename*=utf-8''Test.pptx
...
并且 Joplin REST 服务创建了一个新资源...