7

Why doesn't Qt5 encode plus sign to %2B?

I've tried this code:

QUrlQuery urlQuery;
urlQuery.addQueryItem("test", "hello+world");
manager->post(request, urlQuery.toString(QUrl::FullyEncoded).toUtf8());

But on server's side I always get string like:

hello world

without %2B and without plus sign. So I can't get a '+' sign on the server side...

How can I send a plus sign with Qt5?

IAmInPLS
  • 4,051
  • 4
  • 24
  • 57
Mike
  • 860
  • 14
  • 24

2 Answers2

7

How can I send a plus sign with Qt5?

Don't use an url for post data, use a QByteArray. It will be sent as is.

EDIT

Additional info for QUrlQuery, the + sign is a special case (from the doc):

Handling of spaces and plus ("+")

Web browsers usually encode spaces found in HTML FORM elements to a plus sign ("+") and plus signs to its percent-encoded form (%2B). However, the Internet specifications governing URLs do not consider spaces and the plus character equivalent. For that reason, QUrlQuery never encodes the space character to "+" and will never decode "+" to a space character. Instead, space characters will be rendered "%20" in encoded form. To support encoding like that of HTML forms, QUrlQuery also never decodes the "%2B" sequence to a plus sign nor encode a plus sign. In fact, any "%2B" or "+" sequences found in the keys, values, or query string are left exactly like written (except for the uppercasing of "%2b" to "%2B").

So if you want to use QUrlQuery for strings containing + signs, it seems you have to do the encoding yourself ("+" => "%2B"), you can use the static method QUrl::toPercentEncoding() for that.

Ilya
  • 5,377
  • 2
  • 18
  • 33
  • I've tried how you said. `QByteArray dataToSend("POSTDATA=hello+world"); manager->post(request, dataToSend);` On server's side i got: `POSTDATA: hello world` again without plus sign. – Mike Apr 12 '16 at 07:06
  • @Mike How did you define `ContentTypeHeader`, is it "application/Json" ? If it's Json, could you send your data in a valid Json doc using `QJsonDocument.toJson()` ? – Ilya Apr 12 '16 at 08:07
  • I use `request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");` otherwise I can't send post data with parametrs to server. – Mike Apr 12 '16 at 08:20
  • @Mike. Oh I see, let me check. – Ilya Apr 12 '16 at 08:47
  • Only by myself...is it a bug of qt? Why have other simbols been encoded? – Mike Apr 12 '16 at 09:21
  • It's not a bug, it's on purpose for supporting HTML forms (see explanation), just for space and '+' because they're a special case in browsers. – Ilya Apr 12 '16 at 09:31
  • And NB you also have the static method `QUrl::toPercentEncoding`. – Ilya Apr 12 '16 at 09:43
0

Using PyQt 5 and Python 2.7, this was a bit of a struggle for me to figure out, despite the helpful answer from Ilya elsewhere on this page.

Here's what finally worked for me (OP's example converted to Python2.7/PyQt5):

url_query = QUrlQuery()
url_query.addQueryItem('test', str(QtCore.QUrl.toPercentEncoding('hello+world')))
manager.post(request, str(url_query.toString(QUrl.FullyEncoded)))

And here's a complete example using httpbin.org to check the posted content:

import sys
import json
from PyQt5 import QtCore, QtWidgets, QtNetwork


def slot_finished(reply):
    """ Get form data from reply content """
    reply_content = json.loads(str(reply.readAll()))
    print 'returned: {}'.format(reply_content['form'][KEY])


# Some variables
url = u'http://httpbin.org/post'
content_type = u'application/x-www-form-urlencoded'
KEY = 'test'
data = u'hello + world'

# Build content for the request
url_query = QtCore.QUrlQuery()
url_query.addQueryItem(KEY, str(QtCore.QUrl.toPercentEncoding(data)))
content = str(url_query.toString(QtCore.QUrl.FullyEncoded))

# Post the request and show the reply content
app = QtWidgets.QApplication(sys.argv)
manager = QtNetwork.QNetworkAccessManager()
manager.finished.connect(slot_finished)
manager.finished.connect(app.quit)  # relies on connection order
request = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, content_type)
reply = manager.post(request, content)
print 'expected: {}'.format(data)
app.exec_()

Note: not sure why, but the example also seems to work without explicitly setting QtCore.QUrl.FullyEncoded. Here's what the Qt docs have to say about that constant:

Leave all characters in their properly-encoded form, as this component would appear as part of a URL. When used with toString(), this produces a fully-compliant URL in QString form, exactly equal to the result of toEncoded()

djvg
  • 11,722
  • 5
  • 72
  • 103