calling my XData server --> Unsupported media type: application/x-www-form-urlencoded

The Swagger UI works, but I built a little test app and used the REST Debugger to connect to my service, and I keep getting this error.

I set the Content Type to application/json, but the error says it's really a url-encoded form.

I'm using the TRESTClient/Request/Response components, and this is all I can get.

The headers seem to match what the Swagger interface is showing.

What's the problem? What am I missing?

Swagger headers:
access-control-allow-origin: *
content-encoding: gzip
content-type: application/json
date: Fri,13 Jan 2023 05:55:38 GMT
server: Microsoft-HTTPAPI/2.0
transfer-encoding: chunked
xdata-version: 2

my app headers:
Date=Fri, 13 Jan 2023 08:14:43 GMT
Status Code: 415
Status Text: Unsupported Media Type

My response from the service:
Content Length: 148
Content Type: application/json
"error": {
"code": "UnsupportedMediaType",
"message": "Unsupported media type: application/x-www-form-urlencoded"

It looks like a problem with your TRestClient configuration. It seems it's sending the content type as application/x-www-form-urlencoded.

Yes, I know. The question is WHY? I have it set for application/json wherever I can set ContentType in the Object Inspector and I cannot override it in code since the property cannot be assigned to. It's being over-written somehow. I've found lots of people complaining about this and there was a problem with older versions of the TREST components that was supposedly fixed.

Perhaps there's another setting that forces a override on it?

A post was merged into an existing topic: short-term persistence across several service calls

I don't know, I"m not very familiar with TRESTClient as I mostly use our own THttpClient from Sparkle. But well, try to change all properties from code and post it here so we at least can have an idea of what you're doing without guessing.

Ok, I hooked up Sparkle's THttpClient and fed it this:

    Request := Client.CreateRequest;
    Request.Uri := 'http://localhost/MyService/DoThis';
    Request.Method := 'POST';

    Request.Headers.SetValue('aFocus1', 'aaaaa');
    Request.Headers.SetValue('aFocus2', 'bbbbb');
    Request.Headers.SetValue('aFocus3', 'ccccc');
    Request.Headers.SetValue('aFocus4', 'ddddd');
    Request.Headers.SetValue('aNumScenes', '3');

    Request.SetContent(TEncoding.UTF8.GetBytes('Request content'));
    Response := Client.Send(Request);
    ResponseBody := TEncoding.UTF8.GetString(Response.ContentAsBytes);
    Memo1.Lines.Add( 'StatusCode = '+Response.StatusCode.ToString+' --> '+Response.StatusReason );
    Memo1.Lines.Add( 'ResponseBody:' );
    Memo1.Lines.Add( ResponseBody );

And this is what I got:

StatusCode = 400 --> Bad Request
    "error": {
        "code": "InvalidJson",
        "message": "Value expected but invalid character found at $"

That seems a little more informative than the previous error, but I still don't know what to make of it. Note that I have not changed the service side at all; I'm just trying to get the client to talk with it. And the Swagger interface still works fine for the same POST parameters.

As an aside, the doc is a little unclear about SETTING header details. How do you SET the Accept and ContentType?

UPDATE: I can run this as a GET operation and it works. So there's something weird about how the parameters are being sent.

Also, I tried adding these, but they didn't make any difference.

  Request.Headers.SetValue('accept', 'application/json');
  Request.Headers.SetValue('Content-Type', 'application/json');

Well, you are sending an invalid JSON, and that's what the message says to you: you have an invalid character at the beginning of the text. Send a valid JSON instead of Request content and it should work.

But I'm not sending any JSON. I'm just posting name-value pairs into the header via Request.Header.SetValue(...). I'm assuming it's turning them into JSON itself as part of the POST request.

Or am I not constructing the Request properly? (It would be nice to see some examples in the doc there; it's just discussion.)

Also, do I need the SetValue for accept and content-type?

You are sending this. Why are you doing that? How is your service implemented? What DoThis expects?

I don't really know what this stuff is for. Please show me some examples of POST and GET requests using Sparkle's THttpClient that I can follow. There are just fragments in the Sparkle docs and the same material is in the XData docs.

I find this whole topic quite annoying because there are nearly a half-dozen different components and libraries that people show as examples to do the same types of REST calls. (Three that come with Delphi, plus two versions of Indy, ICS, kbMW, and many others.) My experience is that some work in certain situations and others don't, for reasons I don't understand and are never clearly explained. Worse, there seems to be an overriding expectation that everybody working with these understands what all of the headers are for, why they're used, which ones NOT to use, acceptable and unacceptable options and combinations of options, and how they vary between GET and POST and the other method types one might use.

For one of my calls, I'm forwarding it to a 3rd-party site, and the data that comes back is a well-formed JSON packet that my code consumes and processes just fine when I call that service directly. I'm simply returning that as a string to the caller from my XData service and ... it can't be parsed. The only difference is that I have this XData service in between now. What's being received by my service (in the middle) is fine, but what's being sent back to the client is not a valid JSON packet. Don't ask me why, because I have no idea why it's being messed up. It's being wrapped in a { \r\c value: "{ .... }" \r\c } thing and the data inside of that is chock full of backslashes to escape every quote and brace in the packet. I tried three different JSON parsers to read this, but none of them would. I had to strip off this wrapper and all of the backslash characters added to the data in order to use it. I don't know why this happened, how to stop it, or anything else. It's just what I'm getting back from my service. (The request to the external service comes back as a perfectly valid JSON string, and I simply return it as a string to the caller. Something under the hood is going through some effort to encapsulate it in a way that's making it useless.)

I can't even get Delphi's REST Debugger to work with my XData POST requests that return JSON data. Curiously, I do get back PODOs just fine. :)

I'm asking YOU why this stuff is happening. I have no idea. The code that's working was adapted from Holger's XData course and template.

I forgot to mention that my code was adapted from the Sparkle example given where a POST request was used instead of a GET request that would have sufficed. That's not a big deal, but it did not include sending any parameters to the host, and it DID include the line you asked me why I used it. The answer is, "because you did". My requests work when I submit them as GET requests, but not POST requests -- and I don't understand why not.

I'm not trying to be critical; there's just not enough information in this example to help me figure out what's missing. I feel like I'm shooting in the dark trying this and that, and hoping something works.

Oh ... you asked earlier, "What does DoThis expect?" A List of strings. (TList<string>)

uses {...}, Sparkle.Http.Client;

  Client: THttpClient;
  Request: THttpRequest;
  Response: THttpResponse;
  ResponseBody: string;
  Request := nil;
  Response := nil;
  Client := THttpClient.Create;
    Request := Client.CreateRequest;
    Request.Uri := 'http://myserver/customers';
    Request.Method := 'POST';
    Request.SetContent(TEncoding.UTF8.GetBytes('Request content'));
    Response := Client.Send(Request);
    if Response.StatusCode = 200 then
      ResponseBody := TEncoding.UTF8.GetString(Response.ContentAsBytes);

Well, that's expected. Those components deal with HTTP requests, how to perform then. It's assumed that you should know about an HTTP request, that is consists of a requested URL, headers, and a content body, and the response contains a status code, headers, and a response body. Now, which headers and which content to put is completely up to you, it depends on the server you are connecting to and what kind of headers/content they expect and what kind of headers/response they return.

It helps if you provide more objective information like the exact code you are using, the exact data you are sending/receiving, etc.. Luckily, I can guess what's going on in this specific case, you probably implemented a XData service that returns a string, and you are trying to return a JSON string from that.

function IMyService.SomeJson: string;

This will return an encoded string wrapped in a value property of a JSON object, as stated in the documentation: Service Operations | TMS XData documentation

By the way, I recommend you read this whole topic: Service Operations | TMS XData documentation

Because it explains exactly what kind of HTTP request XData expects and returns based on how you declared your service contract. Having that information you then should know what kind of HTTP request you should build (the correct headers and body) so that your raw HTTP request to your XData endpoint works correctly.

This is just conversation. Please provide exact information about what works and what not, factual data, so it's easier to help without guessing.

I'm asking what the XData endpoint expects as an input? Server-side. Your request is completely wrong, again, why are you sending a request body with the content "Request content"? What does that mean? The XData endpoint probably doesn't expect or require any of that. Why are you sending it?

What is http://myserver/customers? Is it an endpoint of a XData server? If yes, how is implemented? What is the declaration of the service contract of such endpoint (the method signature)?

Or is it an automatic CRUD endpoint based on an entity? If it is, then it expects a JSON with the representation of a customer that is about to be created (inserted) in the database.

That code is copied verbatim from the example code in the Sparkle doc. I didn't write it. I don't know why some of that stuff is there, but I'd like to. POST requests aren't required to send params, but they frequently do. So showing how you'd send different types would be informative. Also on the return side, there seems to be differences in whether you get back a string, an integer, or a collection of things in a JSON packet. The string isn't apparently just a string, if what you're saying is correct. And what's the difference in submitting parameters if the service expects JSON or not? This example leaves me with far more questions than answers, and that's why I'm confused.

Many other HTTP client components have separate "Header" and "Parameter" calls to keep the metadata separate from user data. But this interface apparently doesn't. Again, without any parameters being sent to the service in the one example, I'm unclear what to do with them, or if they're supposed to be name:value or name=value pairs or some kind of JSON expression. What I'm pretty sure of is that the metadata that goes into the header is NOT a JSON expression -- ie, something wrapped by { ... }.

Could you please show how to send a few different types of parameters to the service via that POST request so I can see what's required? I need to send four strings and an integer, and I get back a TList<string>. Here's what my code is doing, but it's not working. I don't know if it's on the client side, the service side, or both. But for a simpler (single-parameter) interface set up the same way that just gets back a single string (a long JSON packet), changing it to a GET request on both sides works fine. (I just have to strip off the JSON wrapper around the embedded 'value' JSON string that's returned.)

If I'm returning a large JSON packet, is there a way to return it without having it get wrapped inside of another JSON packet? What type would I use other than 'string'?

    // this is what I created based on the example, with some guessing on my part
    // there's obviously something wrong, I just don't know what
    Request := Client.CreateRequest;
    Request.Uri := 'http://localhost/myservice';
    Request.Method := 'POST';
    Request.Headers.SetValue('Quote1',    LabeledEdit1.Text);
    Request.Headers.SetValue('Quote2',    LabeledEdit2.Text);
    Request.Headers.SetValue('Quote3',    LabeledEdit3.Text);
    Request.Headers.SetValue('Quote4',    LabeledEdit4.Text);
    Request.Headers.SetValue('NumVariants', NumVariants_spinner.Value.ToString);
    // I don't know if these are required or not; they don't seem to 
    // make any difference either way
    Request.Headers.SetValue('accept', 'application/json');
    Request.Headers.SetValue('Content-Type', 'application/json');

    // these are straight from the example; I cannot explain them beyond 
    // what's in the doc, and I'm guessing they're required
    Request.SetContent(TEncoding.UTF8.GetBytes('Request content')); 
    Response := Client.Send(Request);
    ResponseBody := TEncoding.UTF8.GetString(Response.ContentAsBytes); 

    Memo1.Lines.Add( 'StatusCode = '+Response.StatusCode.ToString+' --> '+Response.StatusReason );
    Memo1.Lines.Add( 'ResponseBody:' );
    Memo1.Lines.Add( ResponseBody );

This returns a list of strings

  TListofString = TList<string>;

  function myservice( Quote1, Quote2, Quote3, Quote4 : string; 
           NumVariants: integer ) : TListofString;
  function myservice( Quote1, Quote2, Quote3, Quote4 : string; 
           NumVariants: integer ) : TListofString;
    . . .
    Result := TListofString.Create;
    // I loop over a TStringlist and add each item to the Result

The return value is a function of the input params.

I'm just shooting in the dark here, trying different things. I don't know what's missing.

It would be really helpful to see an example that shows sending a string and an integer parameter being sent using the POST method to an XData service, what the interfaces on the XData side look like, What the implmetation looks like, and show what's done to return a TList that's already a JSON object and not encapsulated inside of another one as a "value".

It's all described in the documentation topic I sent you. It's a broad topic. If you have a very specific question about a specific situation, let me know and I'd be glad to help. Otherwise, in my opinion there is no sense in giving a generic and broad explanation here about what's already covered in the documentation.

It's described here: Service Operations | TMS XData documentation

About the specific example you provided, as explained in the documentation, the parameters are expected to be received in the request body by default, because it's a POST request. Then you should send a JSON object with the parameters as properties:

    '{ "Quote1": "First", "Quote2": "Second", "Quote3": "Third", "Quote4": "Fourth", "NumVariants": 5'

If you want to return a raw value that is not processed by XData, then your method should return a TStream and then the stream content should be your JSON string.
Or return a TJSONValue (or TJSONObject) with the exact JSON content you want.

THANK YOU for this! Reading a description of something and seeing an example of it are very different mental processes for most people. I'm a visual thinker and one word in a long detailed description is not often enough to trigger a difference in my thinking that an example would clearly reflect. I could read and re-read that description until the cows came home, and I still wouldn't get it! I'd like to suggest you add an example that shows what you did above illustrating how to send multiple parameters of different types in a POST request.

(I was a math major in school, and a lot of classes taught us to work with proofs. I'd often get lost reading proofs because I'd miss the impact of a single word here or there, or just one symbol among a sea of them, and it would drive me nuts. But show me some contrasting equations and I'd get it instantly. Same problem is at work here.)

Most components refer to "Header" and "Body", so "SetContent" did not occur to me as referring to the "Body" part. In fact, I couldn't really grasp what it might be referring to exactly. And it certainly never occurred to me that I'd need to give it a full JSON expression when other components simply want name=value pairs..

Let me ask this: what is it that makes this SetContent example require a full JSON expression rather than some name=value pairs that are usually put into the Request's Body? Analogous to this:

  Request.Body.Add( "Quote1", "aaaaa" );
  Request.Body.Add( "Quote2", "bbbbb" );
  Request.Body.Add( "Quote3", "ccccc" );
  Request.Body.Add( "Quote4", "ddddd" );
  Request.Body.Add( "NumVariants", 5 );  // or "5" as a string

Is this approach possible with this component?

Here is the example: Service Operations | TMS XData documentation

Because that's what it is, is something that has to be sent via the request body. It's not up to the HTTP communication component to deal with JSON, but raw HTTP requests. The content can be JSON, url form encoded, XML, can be a variety of other formats, it's out of the HTTP component scope to deal with it.

In any case, a side question: why are you struggling to invoke XData endpoints with raw HTTP from Delphi, if XData offers the TXDataClient class which abstracts all that for you?

Because earlier when I asked for suggestions you said you just use Sparkle's THttpClient.

There are just too darn many HTTP Client classes around and they're all a little different!

Here's an idea ... you know the New Items option for "TMS XData -> TMS XData Service" that generates the Interface and Implementation units?

How about adding another wizard option that lets you defne a method template that can be pasted into three places: one into each of the Interface and Implementation units, as well as one for the Client side code.

Ask a series of questions and then generate the three bits of code needed to ensure all three are in agreement and contain what's needed to work correctly and produce the results the user wants to get. It could even generate code for a test method on the XData side that lets the user run some tests from the Client side that shows the API works.

  • What client component do you want to use? Sparkle's client, the XData client, the various Delphi client components, maybe others.

  • How many parameters are there and what their types are?

  • Are they JSON or native types?

  • . . . probably more I can't think of

I mean, this entire thread has been an effort on my part to figure this out for one particuilar API call I'm creating. Look at all of the questions you're asking, pointing to stuff in the docs, and confusion on my part. Maybe I'm just being particularly thick-headed about this, but I'm pretty frazzled trying to figure out how to get any one HTTP Client component / class to do this in a simple, straighforward way where all three of these pieces are in agreement. That's really all it has been about.

I can see that the variety can be helpful in different circmstances, but it's awfully confusing if this isn't something you work with very much. At the end of the day it just boils down to whether the three parts are all written in a way that they're all doing the "same dance" so to speak. I don't even care what the "dance" is that they're doing, as long as they do it peacefully and don't step on each other's toes.

In the abstract, this is just another ETL process. Why is it so frigging complicated to come up with a solution that works for a slightly unusual but not terribly complex situation?

I just want something that's going to work without having to become an expert in all of the subtle differences between the various calling options and parameter conversions, along with how to get data in and out of the packets when the two sides are asymmetric in their structure and content. It's just one frigging API call that has consumed some 15 hours of my time so far trying to make it work. (And I'm still not there yet.)

I need to send a POST request that passes four strings (< 80 chars) and an integer to my XData service, and get back a TList<string> where the strings are JSON objects returned from another service. Why is this so complicated to set up, and why does it require so much intimate understanding and knowledge of how all of the various parameters on both sides (HTTP Client component and the XData side) work? I don't think my needs are very complicated. It turns out all of the various options are gumming up the works, and I don't know enough yet to make informed choices. I do not want to have to become an expert in this stuff just to make one single API work!

Consider how much time a Wizard would save in such a situation!