Search code examples
servicestackservicestack-razor

Does ServiceStack support POSTs from plain html?


It is basically does, but I have a problems with national symbols in the POST data. They are came corrupted to the Service.

I have very basic markup:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
    <form action="/hello" method="POST">
        <input name="Name" id="Name"/>
        <input type="submit" value="Send"/>
    </form>
</body>
</html>

Browser sends the following:

Headers:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:uk,ru;q=0.8,en;q=0.6 Cache-Control:max-age=0
Connection:keep-alive Content-Length:41
Content-Type:application/x-www-form-urlencoded
Cookie:ss-pid=s2uF57+2p07xnT9nUcpw; X-UAId= 
Host:localhost:2012
Origin:http://localhost:2012 
Referer:http://localhost:2012/Great
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36

Form Data:

Name=%D0%BF%D1%80%D0%B8%D0%B2%D1%96%D1%82

In the service I receive the following:

РїСЂРёРІС–С

and this.Request.OriginalRequest.EncodingName is "Cyrillic (Windows)". I think it should UTF-8 here. Expected result is

привіт

PS. App.config (I'm using Self-Host) is default from http://www.ienablemuch.com/2012/12/self-hosting-servicestack-serving.html


Solution

  • I've had a look into this and the problem is that the HTTP Listener infers that the Character encoding for the Request as Windows-1251 instead of UTF-8 it does this because the character encoding for the request is specified on the Content-Type HTTP Header, so it would work as expected if you were to change the Content-Type in fiddler to:

    Content-Type: application/x-www-form-urlencoded; charset=utf-8
    

    Unfortunately HTML Forms doesn't let you specify the Content-Type with a charset which would look like:

    <form action="/hello" method="POST" 
          enctype="application/x-www-form-urlencoded; charset=utf-8">
        <input name="Name" id="Name"/>
        <input type="submit" value="Send"/>
    </form>
    

    But browsers effectively ignore this and send the default Form Content-Type instead, e.g:

    Content-Type: application/x-www-form-urlencoded
    

    With the lack of the Content-Type the HTTP Listener tries to infer the Content-Type from the POST'ed data in this case:

    Name=%D0%BF%D1%80%D0%B8%D0%B2%D1%96%D1%82
    

    Which it infers as Windows-1251 and parses the value using that encoding.

    There are a couple of solutions the first is to override the Content Encoding which has just been enabled in this commit and force a UTF-8 encoding, e.g:

    public override ListenerRequest CreateRequest(HttpListenerContext httpContext, 
        string operationName)
    {
        var req = new ListenerRequest(httpContext, 
            operationName, 
            RequestAttributes.None)
        {
            ContentEncoding = Encoding.UTF8
        };
        //Important: Set ContentEncoding before parsing attrs as it parses FORM Body
        req.RequestAttributes = req.GetAttributes(); 
        return req;
    }
    

    This feature will be in v4.0.19 release that's now available on MyGet.

    The second solution is to effectively provide a hint to the HTTP Request to infer the request as UTF-8 which you can do by specifying the first field in English, e.g:

    <form action="/hello" method="POST">
        <input type="hidden" name="force" value="UTF-8"/>
        <input name="Name" id="Name"/>
        <input type="submit" value="Send"/>
    </form>
    

    There is nothing special about force=UTF-8 other than its English and uses the ASCII charset.