delphic++builderc++builder-xe5

C++ Builder TRestRequest with Unicode parameter


I am using TRestRequest to get data from a server. I need to fill a parameter with a Unicode string value: "ôpen". However, I get a crash calling Execute with this Unicode string as a query parameter.

My code :

    RESTRequest->ResetToDefaults();
    RESTRequest->AddParameter("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8 ", TRESTRequestParameterKind::pkHTTPHEADER);
    //  Get Indexing Status
    RESTRequest->Resource = "XXX/collection1"+ Form1->teReuqestHandler->Text+"?";
    RESTRequest->Method = rmGET;
    // replace all space in name field with '\ '
    UnicodeString lcQuery = Form1->teQuery->Text; // this value should be support french language or ...
    // Body
    RESTRequest->AddParameter("q",lcQuery, TRESTRequestParameterKind::pkGETorPOST);
    // Run
    String str1 = RESTRequest->GetFullRequestURL();
    RESTRequest->Execute(); // here when pass "ôpen" to lcQuery, it crash

How do I correctly add "ôpen" to my URL?


Solution

  • The Content-Type: application/x-www-form-urlencoded; charset=UTF-8 HTTP header has no meaning in a GET request, only in a POST request.

    Embarcadero's REST framework does not handle non-ASCII charsets correctly. Even though it uses Indy internally, it does not utilize Indy's charset handling. As such, in order to send UTF-8 encoded data, you have to either:

    1. encode a UnicodeString to UTF-8 manually and then put the UTF-8 octets back into a UnicodeString so TRESTRequest can send them:

      UnicodeString EncodeAsUtf8(const UnicodeString &s)
      {
          UTF8String utf8 = s;
          UnicodeString ret;
          ret.SetLength(utf8.Length());
          for (int x = 1; x <= utf8.Length(); ++x)
              ret[x] = (WideChar) utf8[x];
          return ret;
      }
      
      ...
      
      RESTRequest->ResetToDefaults();
      RESTRequest->Method = rmGET;
      RESTRequest->Resource = L"XXX/collection1" + Form1->teReuqestHandler->Text;
      RESTRequest->AddParameter(L"q", EncodeAsUtf8(Form1->teQuery->Text), TRESTRequestParameterKind::pkGETorPOST);
      String str1 = RESTRequest->GetFullRequestURL();
      RESTRequest->Execute();
      
    2. encode the parameter data yourself:

      #include <IdGlobal.hpp>
      #include <IdURI.hpp>
      
      RESTRequest->ResetToDefaults();
      RESTRequest->Method = rmGET;
      RESTRequest->Resource = L"XXX/collection1" + Form1->teReuqestHandler->Text;
      RESTRequest->AddParameter(L"q", TIdURI::ParamsEncode(Form1->teQuery->Text, IndyTextEncoding_UTF8), TRESTRequestParameterKind::pkGETorPOST, TRESTRequestParameterOptions() << TRESTRequestParameterOption::poDoNotEncode);
      String str1 = RESTRequest->GetFullRequestURL();
      RESTRequest->Execute();
      

      Or:

      #include <IdGlobal.hpp>
      #include <IdURI.hpp>
      
      RESTRequest->ResetToDefaults();
      RESTRequest->Method = rmGET;
      RESTRequest->Resource = L"XXX/collection1" + Form1->teReuqestHandler->Text + L"?q={q}";
      RESTRequest->AddParameter(L"q", TIdURI::ParamsEncode(Form1->teQuery->Text, IndyTextEncoding_UTF8), TRESTRequestParameterKind::pkURLSEGMENT, TRESTRequestParameterOptions() << TRESTRequestParameterOption::poDoNotEncode);
      String str1 = RESTRequest->GetFullRequestURL();
      RESTRequest->Execute();
      

    Otherwise, switch to Indy's TIdHTTP component:

    UnicodeString Response = IdHTTP1->Get(L"http://server/XXX/collection1" + Form1->teReuqestHandler->Text + L"?q=" + TIdURI::ParamsEncode(Form1->teQuery->Text, IndyTextEncoding_UTF8));