benmerckx / monsoon

Minimal haxe web framework and embedded webserver
52 stars 8 forks source link

Bundled body middleware removed? #5

Open hamaluik opened 8 years ago

hamaluik commented 8 years ago

The bundle body middleware was removed by c1dcf14 (not sure why either?).

Without it bundled, do we have to manually parse POST bodies? I've got the following working translating the body into a StringMap, but it's extremely clunky and I can't help but feel I'm doing something wrong?

function parseBody(body:String):StringMap<String> {
    var ret:StringMap<String> = new StringMap<String>();
    for(part in body.split('&')) {
        var value = part.split('=');
        ret.set(StringTools.urlDecode(value[0]), value.length > 1 ? StringTools.urlDecode(value[1]) : null);
    }
    return ret;
}

function postUser(req:Request, res:Response) {
    var body:StringMap<String> = new StringMap<String>();

    switch(req.body) {
        case Parsed(parts): {
            trace("info", "parsed body");
            for(part in parts) {
                switch(part.value) {
                    case Value(v): body.set(part.name, v);
                    default: {}
                }
            }
        }
        case Plain(source): {
            trace("info", "plain body");
            var b:Buffer = Buffer.alloc();
            source.read(b).handle(function(outcome:Outcome<Progress, Error>) {
                switch(outcome) {
                    case Success(data): {
                        body = parseBody(b.content().toString());
                        trace("log", body);
                    };
                    case Failure(failure): {
                        trace("error", failure);
                    };
                }
            });
        }
    }

    res.send('ok');
}
hamaluik commented 8 years ago

For now I've implemented this in a static extension (see below), but it still feels clunky. Considering reading request bodies is a pretty standard operation for web apps, I feel that something like this should be included by default (or if it is, it maybe needs to be documented?)

package;

import haxe.ds.StringMap;
import haxe.io.BytesOutput;
import tink.core.Error;
import tink.core.Future;
import tink.core.Outcome;
import tink.io.Buffer;
import tink.io.Progress;
import tink.http.Request;
import tink.io.Sink;

class RequestTools {
    public static function parse(body:IncomingRequestBody):Future<StringMap<String>> {
        var f:FutureTrigger<StringMap<String>> = Future.trigger();

        switch(body) {
            case Parsed(parts): {
                var ret:StringMap<String> = new StringMap<String>();
                for(part in parts) {
                    switch(part.value) {
                        case Value(v): ret.set(part.name, v);
                        default: {}
                    }
                }
                f.trigger(ret);
            }
            case Plain(source): {
                var b:Buffer = Buffer.alloc();
                source.read(b).handle(function(outcome:Outcome<Progress, Error>) {
                    var ret:StringMap<String> = new StringMap<String>();
                    switch(outcome) {
                        case Success(data): {
                            var raw = b.content().toString();
                            for(part in raw.split('&')) {
                                var value = part.split('=');
                                ret.set(
                                    StringTools.urlDecode(value[0]), 
                                    value.length > 1 ? StringTools.urlDecode(value[1]) : null
                                );
                            }
                            f.trigger(ret);
                        };
                        case Failure(failure): {
                            trace("error", failure);
                            f.trigger(ret);
                        };
                    }
                });
            }
        }

        return f.asFuture();
    }
}
benmerckx commented 8 years ago

Monsoon is currently in an inbetween state. The underlying tink_http has had some api updates and I have not had the time to adjust monsoon correctly. Handling the body is something which will be provided by monsoon later on. I'm working on a rewrite but it will take some time, and I'm also on a holiday for the next two weeks. If you're looking for something to use right now you could check out tink_web.

hamaluik commented 8 years ago

Ahh, I figured as much. For being in-between, it still works quite well! I'm not trying to nitpick or demand anything either, I totally get it.

Is there anything I can do to help, or would that end up being more of a bother than is worth?