[REST API] Form URL Encoded Requests - 'List' Type Input Parameter Error

The following combination isn't allowed when consuming REST API Methods:

[HTTP Method]= "POST"
[Request Format]= "Form URL Encoded"
[Input Parameter].[Send In]= "Body"
[Input Parameter].[Data Type]= "List<T>


Error Message:
Cannot send a 'List' parameter in the request 'Body' of a method with 'Form URL Encoded' request format. Change the parameter data type or the method request format.


Some very widely used public APIs require this combination (namely, Stripe). Also, this is an allowed HTTP combination of HTTP Operation (POST) / Body Type (Form URL Encoded) / Input Parameter Type (Array). For example:


POST /v1/accounts HTTP/1.1
Host: api.stripe.com
Authorization: Bearer xxxxxxxxx
Content-Type: application/x-www-form-urlencoded

type=custom&requested_capabilities[]=card_payments&requested_capabilities[]=transfers&country=AU&email=user@email.com


This seems like very reasonable logic, to serialise a List to a multiple form inputs. I also can't see a way around it. Can I please get help?

I think you should use an API call like this one here:

https://api.call.url/v1?type={type}&card_payments={card_payments}&requested_capabilities={requested_capabilities}&country={country}&email={email}

This will bring you the possibilty for input variables like in the picture in the annex.

Hi Adam,

This indeed seems not possible out-of-the-box with OutSystems' REST capabilities. However, you can always use the OnBeforeRequest to create the body yourself, e.g. by setting it up as JSON, then converting it to form-encoding.

That said, perhaps you better check the Stripe Forge components before re-inventing the wheel!

SaxoTom wrote:

I think you should use an API call like this one here:

https://api.call.url/v1?type={type}&card_payments={card_payments}&requested_capabilities={requested_capabilities}&country={country}&email={email}

This will bring you the possibility for input variables like in the picture in the annex.

Those are URLEncoded attributes... I'm talking about x-www-form-urlencoded attributes which must be submitted in the HTTP request body. The only input method the API I'm consuming provides.

Kilian Hekhuis wrote:

Hi Adam,

This indeed seems not possible out-of-the-box with OutSystems' REST capabilities. However, you can always use the OnBeforeRequest to create the body yourself, e.g. by setting it up as JSON, then converting it to form-encoding.

That said, perhaps you better check the Stripe Forge components before re-inventing the wheel!

OnBeforeRequest

Using OnBeforeRequest to create the body is an interesting idea, I don't know how that would work? Keep in mind there are 300+ operations in the Stripe API each with dozens of input attributes. It would need to be a generic method which could re-write a structure as form inputs... is that possible? How can I write raw content to the body of a request and still include the x-www-form-urlencoded header?


Stripe Forge components

I was planning on publishing my implementation as a new forge component. The existing ones are either completely outdated, don't have full API coverage (and all not maintained). The one that isn't abandoned is based on web block / native UI controls which access stripe directly from the UI as a direct customer account. I need server side integration. Also if you try and import the official Stripe API openapi spec file, there are literally 100s of methods / structures and enums which need to be setup (yes, im aware some tweaking of the spec file is currently needed to fix some of the problems). But they are all standard openapi specs, which OutSystems just doesn't support.


Stripe.net Assembly

I've also investigated importing the native stripe.net client assembly. It can import the actions, but all the inputs and outputs are set to "Object" rather than creating the actual input/output structure types from the assembly class objects. I'm a bit confused on this, as it seems the Import Web Services from a .NET Assembly - Generated Structures support document says:

When introspecting the .NET assembly, Integration Studio creates a single structure with the same name as the base class. The 'Type' attribute specifies which class the structure represents once instantiated.

This seems to just not be happening? Then the Supported Configurations for Import Actions from .Net Assembly seems to contradict this:

Import Actions from .Net Assembly: Limitations: 

– Cannot import Index Properties
– Cannot import Delegate Types
– Cannot import Structures
– Cannot import Enumerations
– Cannot import Generic Methods (although Generic Types are supported in its parameters and return value)
– Members of Generic Classes and of their inner classes are not imported
– Only static Members of Abstract Classes can be imported

I am going to need to import and maintain like 300+ operations, so maintaining all the structure definitions manually isn't really an option.


Current Position

It's looking more and more like I'll need to write an external shim service which translates the content from application/json to application/x-www-form-urlencoded. There are plenty of examples on how to do it, but then as soon as that is needed, I won't be able to publish it on the forge.

Also, not only does this happen with the LIST type, it also breaks with any STRUCTURE types. While I can work around the second one, it seems like OutSystems isn't supporting best practice API consumption. Stripe have chosen to go with Form URL Encoded inputs on purpose as it allows a much easier and safer consumption method.... e.g. in Postman form attributes are named individual values... where as a json object is just raw.

Hi Adam,

I see you have done a fair amount of research, so I'm not sure I can add much :). What I do find interesting is the link you added to the "best practice API consumption", because the article doesn't seem to support your conclusion. It clearly states that "For complex data (especially array/nested object parameters) (...) consider application/json." It also states that "Sending any kind of complex data with application/x-www-form-urlencoded has historically been...not ideal. The following are two patterns I've seen for sending an array called a with members b and c. Neither of them are particularly great:" which leads me to believe there is no standardized way of sending arrays using form-urlencode. So I kinda agree with OutSystems for not supporting something that's not an industry standard.

That said, this doesn't solve your problem of course. If I were in your shoes, I'd probably create a .NET extension that takes an Object as input, the Object being a Structure or a List of Structures (with a ToObject()), and use reflection to convert it to the desired formurlencoded output. It's a bit of a chore, but in the end probably the only thing you can do to make it work (apart from doing stuff outside OutSystems, which of course I'd advise against).


Kilian Hekhuis wrote:

What I do find interesting is the link you added to the "best practice API consumption", because the article doesn't seem to support your conclusion. It clearly states that

"For complex data (especially array/nested object parameters) (...) consider application/json." It also states that "Sending any kind of complex data with application/x-www-form-urlencoded has historically been...not ideal”

So I kinda agree with OutSystems for not supporting something that's not an industry standard.

I was more referring to the fact stripe and twillo being 2 examples of highly used APIs using forms based content.

Also that it does say that for “mostly flat” (which is how I would characterise the stripe api, with only 1-2% of attributes requiring basic list), that forms are still appropriate. Outright not allowing it when it is a very common industry practice (it is a standard multi-select web form input method, eg hold ctrl to select more than 1 item, this is exactly how it works).

I agree that JSON is a better solution for complex content types. But ideally OutSystems supports common patterns and (if simple enough) reduce restrictions on what type of rest APIs can be used.

Kilian Hekhuis wrote:

If I were in your shoes, I'd probably create a .NET extension that takes an Object as input, the Object being a Structure or a List of Structures (with a ToObject()), and use reflection to convert it to the desired formurlencoded output. It's a bit of a chore, but in the end probably the only thing you can do to make it work (apart from doing stuff outside OutSystems, which of course I'd advise against).

I am inclined to agree. Any thoughts on why structures won’t generate automatically? Is there a way I can create integration extension objects (operations, structures, etc) in integration studio programmatically? Using an OS API? I’m pretty good at scripting, and parsing the assembly interfaces to dynamically generate the objects is something I think I could do to make it easier (and maintainable for when future stripe client assembly versions come out).

Unfortunately I don't know about that, I've never used that functionality. I'm pretty sure however you cannot create Structures other than manually (as opposed to programmatically).

Adam Coulter wrote:

Stripe.net Assembly

I've also investigated importing the native stripe.net client assembly. It can import the actions, but all the inputs and outputs are set to "Object" rather than creating the actual input/output structure types from the assembly class objects. I'm a bit confused on this, as it seems the Import Web Services from a .NET Assembly - Generated Structures support document says:

When introspecting the .NET assembly, Integration Studio creates a single structure with the same name as the base class. The 'Type' attribute specifies which class the structure represents once instantiated.

This seems to just not be happening? Then the Supported Configurations for Import Actions from .Net Assembly seems to contradict this:

Import Actions from .Net Assembly: Limitations: 

– Cannot import Index Properties
– Cannot import Delegate Types
– Cannot import Structures
– Cannot import Enumerations
– Cannot import Generic Methods (although Generic Types are supported in its parameters and return value)
– Members of Generic Classes and of their inner classes are not imported
– Only static Members of Abstract Classes can be imported

I am going to need to import and maintain like 300+ operations, so maintaining all the structure definitions manually isn't really an option.

What about this part? Where the documentation seems to contradict itself?

I'll see if I can find someone at OutSystems that can answer that.

Kilian Hekhuis wrote:

I'll see if I can find someone at OutSystems that can answer that.

Thanks Kilian, any feedback on this yet? Cheers mate.

Not yet, I'll poke again...