[Java] POST data op geen enkele manier uit te lezen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Voor school ben ik bezig met het schrijven van een data API in Java. Middels simpele HTTP GET en POST requests kunnen JSON objecten worden opgehaald en weggeschreven worden. Gisteren avond was ik zo goed als klaar, alles zoals op papier uit geprogrammeerd en het werkt allemaal naar behoren. Er is echter één klein onderdeel waar ik niet uit komt, en wel een zeer belangrijk klein onderdeel, de POST data (dat de JSON string die geschreven moet worden bevat, niets anders) moet doorgegeven worden aan de handler van de request. De headers netjes uitlezen, etc gaat allemaal OK. Echter krijg ik dit niet voor elkaar. Hieronder de basis van de code die de headers uitleest en vervolgens gaat bepalen wat er moet gebeuren met het verzoek (dat werkt dus verder allemaal, alleen moet ik die JSON string nog tevoorschijn zien te toveren):
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
            this.inFromClient = new BufferedReader(new InputStreamReader(this.connectedClient.getInputStream()));
            this.outToClient = new DataOutputStream(this.connectedClient.getOutputStream());

            String requestString = this.inFromClient.readLine();
            String headerLine = requestString;

            StringTokenizer tokenizer = new StringTokenizer(headerLine);
            String httpMethod = tokenizer.nextToken();
            String httpQueryString = URLDecoder.decode(tokenizer.nextToken());

            while (this.inFromClient.ready()) {
                System.out.println(requestString);
                requestString = this.inFromClient.readLine();
            }
            
            // httpMethod en httpQueryString verwerken

Zoals ik al zei, de headers komen netjes tevoorschijn. Ik veronderstelde eerst (heel dom natuurlijk), dat de POST data bij de request headers zouden zitten. Natuurlijk niet, dat is de body. Met de weinige kennis die ik van HTTP requests heb (ik zeg dit met nadruk omdat ik het globale plaatje natuurlijk snap, maar de fijnigheden weet ik er ook niet van), zou ik denken dat de headers eerst komen, elke header eindigt met een linebreak. Headers klaar -> empty line. Daarna zou de POST data als url encoded string moeten komen.

Blijkbaar niet. inFromClient.reader() geeft na de laatste header al aan dat het inkomende verzoek compleet binnen is. Ik ben gisteren meer dan twee uur bezig geweest om hier iets op te vinden, niet gelukt, daarom ben ik nou hier. Tot nu toe heb ik, zowel in de docs van alle betrokken klassen nog niks gevonden waar uit blijkt dat er bijv. uit inFromClient of connectedClient.getInputStream() ook maar enige andere vorm van informatie te trekken die de client mee stuurt. Ook kan ik geen vergelijkbare problemen op StackOverflow vinden (of ergens anders), laat staan voorbeelden die op een vergelijkbare manier omgaan met POST data (dus dan heb ik het niet over multipart data, het is altijd een string en is dat dus niet nodig.

Mijn projectgroep komt er ook niet uit, ze hebben mee gekeken, maar we zijn allemaal vrijwel ten einde raad. In eerste instantie heb ik nog aan m'n klasse getwijfeld die de requests doet naar de server, ik wist namelijk niet zeker of de POST data wel wordt meegestuurd. Daarom heb ik het naar een PHP script doorgestuurd en de POST data uitgelezen, allemaal in orde, request is gewoon OK. Ik heb het eveneens getest met een simpel HTML formulier dat handmatig een verzoek kan sturen. Nog krijg ik op geen enkele manier door wat er mee wordt gestuurd. Ten einde raad. Maar goed, volgens mij ben ik gewoon gek en zie ik iets over het hoofd. Er zullen vast mensen hier rondlopen die net weten hoe het wél moet. Iemand?

offtopic:
De API is puur voor opslag van (relationele) data in de vorm van objecten. Dit mag volgens de projectspecificaties niet gebeuren middels een RDMS en moet in de vorm van FileIO. Dit geheel moet ook nog eens gedeeld worden met andere deelsystemen. De meeste projectgroepen kiezen er voor om gewoon serialized object op Dropbox te pleuren, echter vond ik dit idee toch net een tikje beter. Bovendien vind ik het zeer interessant om (na jaren simpelweg Apache + PHP gebruikt te hebben) zelf een 'webserver' te implementeren.

Acties:
  • 0 Henk 'm!

  • Mammon
  • Registratie: December 2006
  • Laatst online: 24-09 03:04
Heeft de JSON data een regeleinde (\n, \r, \r\n)? Zo niet werkt de readLine methode niet (zoals jij het verwacht).

Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 14-10 10:34
Zelf een HTTP server maken voor school, dat komt me bekend voor :)

Erg leuk om te doen en erg leerzaam, maar zoals je nu merkt is het niet altijd heel simpel. Als je nu gewoon een werkende "data-API" wil hebben zou ik aanraden om Tomcat te gebruiken en een servlet te maken of een framework te gebruiken (ingewikkelder).

Als je toch je HTTP server werkend wil krijgen zul je je erg strikt aan de specificaties moeten houden. HTTP/1.0 is genoeg, dus zoek eens RFC1945 op.

De oorzaak van je probleem zit hem waarschijnlijk in het volgende:

De POST data begint inderdaad na een lege regel na de eventuele headers, maar niet hoeft te eindigen met een regeleinde. De BufferedReader klasse heeft echter wel een regeleinde aan het eind nodig voordat deze een readLine() resultaat teruggeeft. Mogelijk dat je JSON allemaal op een enkele regel staat zonder regeleinde.

Je kan de bytes van de server socket ook niet zomaar in een reader kan stoppen die daar karakters van maakt. De "new InputStreamReader(InputStream in)" constructor is daarnaast ook nog eens gevaarlijk ook, omdat deze de karakterset kiest die "toevallig" de standaard is op het systeem en dus niet altijd gegarandeerd hetzelfde is. Lullig als iemand opeens een speciaal teken wil gebruiken en het gaat verkeerd.

Je zult dus de inhoud van de Content-Length header moeten gebruiken en pas later de bytes omzetten naar karakters. Voor de goede orde dus ook de charset uitlezen.

Daarnaast kan het nog zijn dat als je Ajax gebruikt voor je API dat je met "application/x-www-form-urlencoded" POST data om moet gaan. Dit zul je ook volgens de (minder goed gedefinieerde) specs moeten parsen.

Succes!

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Menen jullie dit serieus?
Dit kan namelijk heel goed het geval zijn geweest. Inmiddels ben ik bezig gegaan en heb ik het totaal omgebouwd, nu maak ik gebruik van HttpServer. Eindelijk werkt alles naar behoren en ik kan tenminste makkelijk HttpExchange.getRequestBody() aanroepen, waarna ik de POST data heb. Kortom, het werkt.

Wegens mijn eindeloze nieuwsgierigheid zal ik zo even mijn originele code nog eens testen en kijken of het met een simpel regeleinde achter de JSON string plakken wel werkt. Als dat zo is, tsja.. dan sla ik mezelf voor m'n kop. In principe vrij logisch dat readLine() een regeleinde nodig heeft. Pff. Ongeveer anderhalve dag verspilt.

Edit:
Verdomme nog aan toe. Damn. Eén regeleinde had me dus anderhalve dag gescheeld.
Enorm bedankt heren.

Ps. Een webserver blijft inderdaad gruwelijk interessant. Ik heb een simpele webserver geschreven voor m'n Arduino, die echter alleen met GET data om kan gaan (meer was niet nodig). Dat was gelukkig een iets simpeler klusje.

[ Voor 20% gewijzigd door GWTommy op 03-10-2012 16:48 ]


Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 14-10 10:34
GWTommy schreef op woensdag 03 oktober 2012 @ 16:42:
Menen jullie dit serieus?
Dit kan namelijk heel goed het geval zijn geweest. Inmiddels ben ik bezig gegaan en heb ik het totaal omgebouwd, nu maak ik gebruik van HttpServer. Eindelijk werkt alles naar behoren en ik kan tenminste makkelijk HttpExchange.getRequestBody() aanroepen, waarna ik de POST data heb. Kortom, het werkt.
Nou is het zo dat classes uit com.sun.* packages zoals HttpServer erg handig kunnen zijn, het wordt afgeraden om deze te gebruiken omdat deze niet standaard onderdeel zijn van Java. Zo zal het op een Linux systeem met OpenJDK mogelijk niet werken.
Wegens mijn eindeloze nieuwsgierigheid zal ik zo even mijn originele code nog eens testen en kijken of het met een simpel regeleinde achter de JSON string plakken wel werkt. Als dat zo is, tsja.. dan sla ik mezelf voor m'n kop. In principe vrij logisch dat readLine() een regeleinde nodig heeft. Pff. Ongeveer anderhalve dag verspilt.
Bonuspunten voor het maken van een goede HTTP parser die dus geen regeleinde van de client nodig heeft :)

Acties:
  • 0 Henk 'm!

  • GWTommy
  • Registratie: Mei 2008
  • Laatst online: 05-08-2023
Gelukkig gaat het om een project voor school en zoals ik vertelde hebben wij toch wel de meest doordachte oplossing van alle projectgroepen. Bovendien zal er voor elk deelsysteem een laptop draaien + één laptop met de server draaiende, allemaal verbonden via een switch. Voor zover ik weet zullen we alleen MacBooks en Windows laptops gebruiken.

Ik vraag me wel af hoe de andere projecten gaan lopen als Dropbox weer gaat lopen rotzooien met conflicted copies. Daarvoor heb ik een hoop ingebouwd om files te locken, etc. Wat een helden, Dropbox voor het opslaan van gedeelde data (serialized objecten).

Acties:
  • 0 Henk 'm!

  • matthijsln
  • Registratie: Augustus 2002
  • Laatst online: 14-10 10:34
Ik geloof graag dat het je server het beste idee is, maar stiekem ben ik wel geintrigeerd door de Dropbox oplossing ;)

Je zal in het bedrijfsleven of 'de praktijk' nog wel brakkere oplossingen tegenkomen :)
Pagina: 1