Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: PanVP 14. 11. 2019, 00:36:19

Název: C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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!
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: oss 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?)
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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  >:(
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: tecka 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š.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Filip Jirsák 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?
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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...
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Filip Jirsák 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?
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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...
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Filip Jirsák 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ář.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Filip Jirsák 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?
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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 ;)
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Martin Dráb 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.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 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
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Martin Dráb 14. 11. 2019, 23:57:13
Citace
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.

Pokud budu programovat zařízení, co komunikuje přes USB na dostatečně nízké úrovni, tak mi nic jiného než jisté seznámení s touto sběrnicí nezbude.

Podobně jako když třeba používám pro síťovou komunikaci sockety, měl bych vědět něco o vrstvách, které jsou pod nimi (zdaleka ne všechno), protože pak různé exotičtější chování není takovým překvapením a vlastně dává smysl.

Citace
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

Tak, v POST požadavku vygenerovaném metodou UploadFile je vidět nějaká struktura, která má zřejmě nějaký svůj vnitřní smysl (tzn. nedá se předpokládat, že by si ji vývojáři zodpovědní za UploadFile vycucali z prstu). RFC jsou sice celkem dlouhá, ale obvykle stačí jen proletět příslušné pasáže. Google samozřejmě také pomůže, ale je třeba být opatrný.

V minulém příspěvku jsem si dovolil drobné popíchnutí, neb je poměrně obtížné čistě neutrálně reagovat na vaše, zejména ty první, příspěvky.

Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: PanVP 15. 11. 2019, 00:20:16
V minulém příspěvku jsem si dovolil drobné popíchnutí, neb je poměrně obtížné čistě neutrálně reagovat na vaše, zejména ty první, příspěvky.

Uznávám, byl jsem z toho vzteky bez sebe a RFC jsem si přečíst mohl, nicméně poctivému Googlování jsem se věnoval dva dny, ale po bitvě je každý generál

Obecně jsem očekával, že když PUT má nějakou hlavičku, že pro POST (což by měl být v podstatě podobný požadavek) to bude stejné. Navíc jsem si svojí, uznávám chybnou, tezi potvrdil CURLem i Pythonem.

Jinými slovy, řadou svých pokusů jsem chybně vyloučil možnost "že by to M$" mohl mít správně.
Včetně toho pokusu, kdy jsem si uložil požadavek do souboru a poslal ho bez "boundary" serveru, který si s tím poradil.

Doplním, že ten ukázkový příklad z dokumentace k API také nefunguje, Chrome tam také rve boundary=-----
Pokud se k tomu dostanu, zkusím i nějakou historickou verzi IE, jestli se něco změní.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Martin Dráb 15. 11. 2019, 00:42:46
Citace
Obecně jsem očekával, že když PUT má nějakou hlavičku, že pro POST (což by měl být v podstatě podobný požadavek) to bude stejné. Navíc jsem si svojí, uznávám chybnou, tezi potvrdil CURLem i Pythonem.

Myslím, že není nikde definováno, že každý POST požadavek má používat multipart/boundary. K tomu se klient uchyluje, pokud chce v rámci jednoho požadavku odeslat více bloků (různých) dat. Při odesílání souboru se do požadavku přidává jak jejich obsah, tak i nějaké doplňující informace (např. jméno na disku klienta).

Nevím, zda PUT nedovoluje použít také podobný přístup (multipart/boundary), ta RFC jsem četl již celkem dávno. Ono je někdy dost rozdíl v tom, co RFC dovoluje/doporučuje/vynucuje a co se v praxi obvykle používá/implementuje.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Filip Jirsák 15. 11. 2019, 08:06:33
Doplním, že ten ukázkový příklad z dokumentace k API také nefunguje, Chrome tam také rve boundary=-----
Pokud se k tomu dostanu, zkusím i nějakou historickou verzi IE, jestli se něco změní.
Nezmění. Z prohlížeče odesíláte formulář, v jehož rámci se uploaduje soubor. To je něco úplně jiného, než odeslání XML dat na server.

V těle HTTP požadavku se mohou posílat libovolná data (v libovolném formátu). Aby server věděl, o jaký formát dat jde, posílá se hlavička Content-Type. Váš REST server očekává jako typ dat na vstupu XML. Prohlížeče při odesílání HTML formulářů používají typ dat pro HTML formulář (existují dva, jeden pro formuláře bez uploadu souboru, druhý pro formulář se uploadem souboru). Váš server s tím formulářovým typem neumí pracovat (což je celkem logické).

Vaše pokusy posílat formulář tam, kde je očekáváno XML, jsou stejné, jako kdybyste se pokoušel otevřít video soubor v textovém procesoru. Když se vám nepovedlo otevřít video v Libre Office, je zbytečné zkoušet to ještě ve starší verzi a v MS Wordu. Prostě textové procesory video otvírat neumí. A stejně tak váš REST server umí zpracovat „soubor“ XML ale ne soubor s HTML formulářem.
Název: Re:C# a POST požadavek nebo náhrada Webclineta
Přispěvatel: Filip Jirsák 15. 11. 2019, 08:17:11
Myslím, že není nikde definováno, že každý POST požadavek má používat multipart/boundary.
Nikde to definováno není, v POSTu samozřejmě může být jakýkoli obsah, stejně jako u všech ostatních metod.

K tomu se klient uchyluje, pokud chce v rámci jednoho požadavku odeslat více bloků (různých) dat.
Ve skutečnosti to používají jenom prohlížeče pro upload souborů z webového formuláře, a pak aplikace, které tenhle upload chtějí simulovat. Žádné jiné použití multipart/form-data jsem neviděl – pokud nějaké API potřebuje získat více částí, dělá se to samostatnými voláními. Ostatně povšimněte si, že ten typ se jmenuje form-data, má to přímo v názvu, že je určen pro odesílání formulářových dat.

Nevím, zda PUT nedovoluje použít také podobný přístup (multipart/boundary)
Povoluje, resp. HTTP standard vůbec neřeší, jaké mime-typy se kde mohou poslat. Ten typ samozřejmě můžete použít, ale musíte vědět, že server ho umí zpracovat. A bude to vaše unikátní řešení – ten typ se normálně používá jen v prohlížečích pro odeslání formuláře se souborem, a pro to se vždy používá metoda POST. Prohlížeče samy o sobě metodu PUT na webu nikdy nepoužívají. (Něco jiného je samozřejmě AJAXový kód v JavaScriptu, tam si zase můžete HTTP požadavky sestavovat, jak chcete. A něco jiného je případná podpora WebDAVu nebo jiných protokolů v prohlížeči.)