C# a POST požadavek nebo náhrada Webclineta

PanVP

C# a POST požadavek nebo náhrada Webclineta
« kdy: 14. 11. 2019, 00:36:19 »
Ahoj,

už třetí den se drbu s tím, že Widlo C# WebClient dělá úplně něco jiného, než chci, tedy:
WebClient webClient = new WebClient();

Snažím se o úplně jednoduchou věc, odeslat data pomocí POST požadavku na webový server.

S příkazem CURL vše 100% funguje, šup za minutu!
curl -X POST -d @import.xml http://127.0.0.1:5000/api/rest/
Tj. pomocí POST požadavku službě posílám data.

Takhle vypadá sestavený požadavek:
Kód: [Vybrat]
POST /api/rest/ HTTP/1.1
Host: 127.0.0.1:5000
User-Agent: curl/7.67.0
Accept: */*
Content-Length: 5335
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue

A všechno funguje na 100%

...ne tak ve světě Microsoftu :-\

Kód: [Vybrat]
webClient.Headers.Clear();
webClient.Headers.Add("Accept" , "*/*" );
webClient.Headers.Add("User-Agent", "DataCON");
webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded" );
webClient.UploadFile(myUri,"POST",
....

A výstup požadavku:
Kód: [Vybrat]
POST /api/rest/ HTTP/1.1
Accept: */*
Content-Type: multipart/form-data; boundary=---------------------8d7689803def3b6
User-Agent: CsharpConnector
Host: 127.0.0.5:5000
Content-Length: 5637
Expect: 100-continue
Connection: Keep-Alive

-----------------------8d7689803def3b6
Content-Disposition: form-data; name="file"; filename="produkt.xml"
Content-Type: application/x-www-form-urlencoded

Kdy všechno mezi boundary=----- je spokojeně ignorováno...  >:(

Nerad používám knihovny třetích stran, už jsem tam dvakrát našel potenciální backdoor, kdy knihovny "volaly domů".

A teď co s tím, napadá mě:
- zachovat se násilně a jít do M$ na lov programátorů (pokud se lov provede, budete mě do konce života živit)
- nějak rozm.... kód webklienta a zjistit, kde se tam ta **** bere, což mi zabere tak měsíc a možná to skončí možností A
- kouknout se po něčem jiném na odesílání dat, kdy mi webklient ...plně stačí

Někdo říká, že M$ je sbírka ........ a že jejich věci jsou jen rozbité demoverze, aby mohli vydávat nové.

Fakt jsem z toho hotovej, prolezl jsem snad už celý internet, protože v celém projektu i v jiných projektech WebClient používám, přecházet na webrequest...ale C# je splácaninou nových a starých tříd, používá se to vůbec?

Fakt mi je z toho zle...  :-\
...CHCI KREV!!! JEJICH KREV!!!

Teď jsem se znova ptal Google a ...jako napotvoru našel.
Našel jsem info, že problém způsobuje použitá metoda "UploadFile" a mám použít: UploadData
Citace
You should use UploadData method of WebClient class instead of UploadFile.

Ale tu krev chci stejně! JEJICH KREV!
« Poslední změna: 14. 11. 2019, 00:42:13 od PanVP »


oss

  • ***
  • 229
    • Zobrazit profil
    • E-mail
Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #1 kdy: 14. 11. 2019, 07:14:27 »
Aha takze pouzivas zastarale API, spravis v kode chybu, ktoru odhali prve googlenie ale je to vina Microsoftu...

 - Kod WebClienta je pre .Net Core na githabe, pre starsie dotnety na https://referencesource.microsoft.com/ tiez to odhali prvy dotaz na google
- Novsie API na HTTP requesty sa prekvapivo vola HttpClient (kto by to cakal?)

PanVP

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #2 kdy: 14. 11. 2019, 20:33:13 »
slinty

Ty asi nemáš moc kamarádů viď?  ;D

Jednak pro nahrání souboru se použít funkce webClient.UploadFile.
To, co jsem našel, je jen možnost jak obejít bug.
Tj. ručně musím načíst XML, převést to na stream, pohlídat kódování, přehodit na byte array a pak uploadnout pomocí funkce UploadData.

Ale, když už jsme u toho, jak si Kefalín představujete použití třídy HttpClient co?
Jako Hurvínek válku co?
By the way, s HttpClient nefungují jiné části projektu, takže běž do Brusele nebo Microsoftu.

PanVP

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #3 kdy: 14. 11. 2019, 20:43:58 »
A jen tak pro doplnění, zatímco UploadFile i ostatní funkce očekávají jako první parametr URI, tak UploadData trvá na STRINGU... kvalita kódu jako stehno  >:(
« Poslední změna: 14. 11. 2019, 20:48:48 od PanVP »

tecka

  • ***
  • 138
    • Zobrazit profil
    • E-mail
Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #4 kdy: 14. 11. 2019, 21:38:42 »
Ta metoda dělá přesně to co má. Má variantu, kde se adresa zadává jako Uri. A to, že s jiným řešením nefungují jiné části kódu je fakt perla. Nevíš, co děláš, na dokumentaci ses ani napodíval a každým příspěvkem se ztrapňuješ.


Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #5 kdy: 14. 11. 2019, 22:14:58 »
Proč tam ručně cpete Content-Type form-urlencoded, když chcete uploadovat soubor, tedy multipart/form-data? Ta metoda tam pošle správnou hlavičku, ale vy to pak přebijete svou špatnou hlavičkou.

Taky byste si měl ujasnit, co vlastně chcete posílat. Pomocí curl posíláte obsah souboru, v kódu pak ale uploadujete celý soubor. Chcete tedy posílat data nebo soubor?

PanVP

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #6 kdy: 14. 11. 2019, 22:34:14 »
Proč tam ručně cpete Content-Type form-urlencoded, když chcete uploadovat soubor, tedy multipart/form-data? Ta metoda tam pošle správnou hlavičku, ale vy to pak přebijete svou špatnou hlavičkou.

Taky byste si měl ujasnit, co vlastně chcete posílat. Pomocí curl posíláte obsah souboru, v kódu pak ale uploadujete celý soubor. Chcete tedy posílat data nebo soubor?

Velmi dobrá poznámka ohledně Content-Type, to cílové API není moje.
Content type jsem zkoušel upravit tak, abych se dostal na stejný výstup, jaký leze z CURL.
Protože s CURL to funguje na první dobrou.
Mimo to, Content-Type je zjevně ignorován.

Jen pro zajímavost:
Tohle funguje: webClient.UploadFile(myUri, "PUT", @"nejake.xml");
Tohle nefunguje: webClient.UploadFile(myUri,"POST", @"nejake.xml");

Hlavička v případě požadavku PUT:
Kód: [Vybrat]
PUT /api/rest/ HTTP/1.1
Content-Type: application/octet-stream
Host: 127.0.0.5:5000
Content-Length: 5433
Expect: 100-continue
Connection: Keep-Alive

Hlavička v případě požadavku POST:
Kód: [Vybrat]
POST /api/rest/ HTTP/1.1
Accept: */*
Content-Type: multipart/form-data; boundary=---------------------8d7689803def3b6
User-Agent: CsharpConnector
Host: 127.0.0.5:5000
Content-Length: 5637
Expect: 100-continue
Connection: Keep-Alive

-----------------------8d7689803def3b6
Content-Disposition: form-data; name="file"; filename="produkt.xml"
Content-Type: application/x-www-form-urlencoded

Odesílá se stejný soubor stejné službě.
PUT pro aktualizaci, POST pro založení nového záznamu.
Tj. fakticky se jedná o stejné XML zasílané stejnému API, ale v tom pro PUT musí být ID, naopak v tom pro POST nesmí.

Proč se ta hlavička takhle změní ::)
Nebo, jestli to je správně, to ....mi hlava nebere.

Že se stejná funkce chová jinak jen na základě toho, že PUT změním na POST  ::)
Tohle funguje: webClient.UploadFile(myUri, "PUT", @"nejake.xml");
Tohle nefunguje: webClient.UploadFile(myUri,"POST", @"nejake.xml");

Převedením souboru na stream a jeho odesláním se problém vyřešil, ale teda...
« Poslední změna: 14. 11. 2019, 22:38:47 od PanVP »

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #7 kdy: 14. 11. 2019, 22:53:08 »
Když použijete POST, dělá ta metoda klasický formulářový upload souboru. Když teda odstraníte ten váš Content-Type a necháte metodu vložit tam správný. Otázkou samozřejmě je, zda cílové API očekává formulářový upload souboru.

Žádný prohlížeč neodesílá formuláře metodou PUT, takže pro tuto metodu není upload souboru nijak definován. Podle vašeho výstupu metoda dělá to, že v těle požadavku odešle obsah souboru. Takové chování dává smysl.

Podle vašeho popisu je to ale klasické RESTové API, které POSTem vytváří nový záznam a PUTem přepisuje existující záznam. Takové API bude určitě fungovat s tím, že dostane v těle požadavku obsah – tak, jak to máte s tím curl. teoreticky tam může být nějaké berlička, že to bude přes POST akceptovat i upload souboru, aby se to dalo otestovat i v prohlížeči, ale to byste si musel ověřit, že to tak opravdu funguje (třeba aspoň tím curl). Ale spíš bych se na to vykašlal a posílal bych data normálně v těle požadavku.

A pokud tam posíláte XML, doporučoval bych Content-Type nastavovat na application/xml (nebo text/xml). Server teď asi Content-Type ignoruje – když mu pošlete, že je to formulář a místo toho pošlete XML, on to akceptuje. Ale třeba to bude v nějaké příští verzi opravené a ten váš rozbitý kód pak přestane fungovat.

Převedením souboru na stream a jeho odesláním se problém vyřešil, ale teda...
Když jste si uvědomil, že nechcete dělat formulářový upload souboru, ale poslat data, opravil jste kód, aby to skutečně dělal, a ono to funguje. Co je na tom divného?

PanVP

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #8 kdy: 14. 11. 2019, 23:06:17 »
doporučoval bych Content-Type nastavovat na application/xml (nebo text/xml)

Tak jsem to měl, ale POST tam natvrdo nacpe: Content-Type: multipart/form-data

To paradoxně vůbec nevadí, API to zřejmě ignoruje, ale vadí to, že se tam objeví boundary=----
"Content-Type: multipart/form-data; boundary=---------------------8d7689803def3b6"
Podle všeho, cokoliv po boundary je ignorováno až do koncového "-----------------------8d7689803def3b6"

Kód: [Vybrat]
POST /api/rest/ HTTP/1.1
Accept: */*
Content-Type: multipart/form-data; boundary=---------------------8d7689803def3b6
User-Agent: CsharpConnector
Host: 127.0.0.5:5000
Content-Length: 5637
Expect: 100-continue
Connection: Keep-Alive

-----------------------8d7689803def3b6
Content-Disposition: form-data; name="file"; filename="produkt.xml"
Content-Type: application/x-www-form-urlencoded

Když jsem stejnou věc cvičně zkoušel z Pythonu, tak POST i PUT se chovají stejně, resp. POST tam žádnou sekci "boundary=------------------" nerve. Nejsem až tak dobře obeznámen s detaily implementace HTTP protokolu, abych se mohl hádat o tom, jestli tam to boundary patří nebo ne.

A stačí udělat jednu věc:
Požadavek POST odeslaný z mojí aplikace z WebClienta zachytit NETcatem, uložit do souboru tj. nc -l -p 5000 > pozadavek.txt, odebrat, boundary=---------------------8d7689803def3b6 a koncové -----------------------8d7689803def3b6, no a funguje to.

Jak říkám:
Tohle funguje: webClient.UploadFile(myUri, "PUT", @"nejake.xml");
Tohle nefunguje: webClient.UploadFile(myUri,"POST", @"nejake.xml");

Jediný rozdíl, PUT tam nerve to boundary, POST ho tam rve...

To API si jednoduše ignoruje všechno uvnitř té sekce  ::)
Co to je zač, ta =---------------------8d7689803def3b6 a -----------------------8d7689803def3b6... klidně mě poučte  ::)

A jestli jsem opravil aplikaci ... to nevím, potřebuji tomu API podat soubor z lokálního disku.
No a obráceně, kdybych té aplikaci chtěl podat stream, tak bych určitě nepovažoval za správné ho nejprve uložit do souboru a pak ručně odeslat...
« Poslední změna: 14. 11. 2019, 23:14:05 od PanVP »

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #9 kdy: 14. 11. 2019, 23:10:36 »
Ano, to boundary tam patří. Je to způsob, jakým je implementován upload souborů přes formuláře v HTML (v prohlížečích).  typ je multipart/form-data, který umožňuje posílat v jednom požadavku víc částí, boundary definuje hranici mezi těmi částmi a každá část je pak buď jedno formulářové pole nebo uploadovaný soubor.

Ale pokud je to klasické RESTové API, nejsou tam žádné formuláře a veškerý obsah se posílá v těle požadavku. Takže tam není žádný upload souboru a nebudete používat metodu UploadFile. Ta je určená pro upload souboru tak, jak to dělají webové prohlížeče s uploadem souboru přes formulář.

PanVP

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #10 kdy: 14. 11. 2019, 23:18:22 »
Ano, to boundary tam patří.

Aha...takže vlastně brečím na špatném hrobě (ale M$ si tak jako tak zaslouží), jestliže přímo v dokumentaci stojí, že API mám poslat data pomocí metody POST, asi by se s tím boundary měla vyrovnat ne? ::)

Ale pokud je to klasické RESTové API
V dokumentaci od API je i HTML stránka (krátké testovací HTML) pro odeslání dat pomocí formuláře, asi (jako já) ještě nečetli o boundary  :P

Kdyby nebylo půl dvanácté, tak bych se mrknul, jak vypadá požadavek odeslaný pomocí toho testovacího formuláře.
« Poslední změna: 14. 11. 2019, 23:24:54 od PanVP »

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #11 kdy: 14. 11. 2019, 23:23:27 »
Ne. Když je to RESTové API, očekává se, že v těle požadavku jsou rovnou data. Ne že posíláte obsah webového formuláře. Je to API, ne web. Ale mělo by být zdokumentováno i to, co se má posílat v těle požadavku. Jak jste přišel na to, že tam máte posílat zrovna XML a v jakém formátu? Proč tam neposíláte nějaký divnější formát, třeba JSON? To jste nevyčetl z dokumentace toho API?

PanVP

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #12 kdy: 14. 11. 2019, 23:30:39 »
Proč tam neposíláte nějaký divnější formát, třeba JSON?

JSON všichni milují :P
Někdo...jako já, na tom provozují třeba i Mongobáze  :P

Tohle api umí jen XML a CSV...také ho dělali Francouzští Indové... dokumentace je celkem dobrá, jen občas tam jsou věci jako संस्करण 2-1b ::)

Každopádně, Filipe, děkuji ;)
« Poslední změna: 14. 11. 2019, 23:32:32 od PanVP »

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #13 kdy: 14. 11. 2019, 23:32:21 »
Citace
jestliže přímo v dokumentaci stojí, že API mám poslat data pomocí metody POST, asi by se s tím boundary měla vyrovnat ne?
Myslím, že různé metody komunikace (GET, POST, PUT...) najdete popsané v RFC dokumentech týkajících se protokolu HTTP. Jelikož pomocí něho posíláte data, možná byste měl věnovat trochu času tomu, jakými možnostmi tento protokol disponuje, aby vaše slzy nepadaly na špatný hrob. Rozhodně je to užitečnější (do budoucna) než náhodné googlení a nadávání.

Ta boundary je prostě součást protokolu a používá se obecně v případě, že chcete v rámci jednoho požadavku poslat více datových částí. Nejvíce to bude vidět samozřejmě u formulářů a uploadu souborů.

Je ale samozřejmě pravda, že v RESTu se nepohybuji, takže tam třeba panují jiné zvyklosti.

PanVP

Re:C# a POST požadavek nebo náhrada Webclineta
« Odpověď #14 kdy: 14. 11. 2019, 23:37:02 »
Je ale samozřejmě pravda, že v RESTu se nepohybuji

Tak to jsme dva.

A doufám, že až příště začnete na něčem pracovat, důkladně se seznámíte s protokolem pro USB, abyste věděl, jak s počítačem komunikuje myš a klávesnice, se všemi protokoly síťové vrstvy a s veškerou dostupnou dokumentací pro úplně všechno co ve vašem projektu je. Vyhnete se tak problému, kdy budete řešit podružnou věc, kterou zkusmo otestujete příkazem CURL a budete si myslet, že stačí poslat stejný požadavek  ;D