Open
Description
Thank you for your work on this awesome library!
Should we be able to pass JSON strings in the Body of a POST request? Am I not using the Client correctly?
- I get a 200 ResponseCode in the README example.
- I get a 200 ResponseCode on POST when passing Body a variable path to a valid DUT.
- I get a 400 ResponseCode on POST when passing Body an empty string (
Body := ''
) because my endpoint validates the request. - I get a 200 ResponseCode when using curl with valid JSON data.
- I do not get a ResponseCode on POST when passing Body a JSON string with valid JSON data.
Failing example with JSON string
MAIN.TcPOU
Client : HttpClient;
Execute : BOOL := TRUE;
HasError : BOOL;
ErrorId : UDINT;
Client(
Execute := Execute,
Address := 'https://www.fake-example.com/ExampleController/Post',
CallMethod := 'POST',
Body := '{"stringProperty": "string", "dintProperty": 1, "arrayProperty": ["string"]}',
ResponseCode := 'GVL.ResponseCode', // This is not updated from the default value of 0.
Response := '',
HasError => HasError, // No error returned
ErrorId => ErrorId);
IF Execute THEN
Execute := FALSE;
END_IF
GVL.TcGVL
{attribute 'qualified_only'}
VAR_GLOBAL
ResponseCode : INT;
END_VAR
header.json
{
"Accept": "*/*",
"Content-Type": "application/json"
}
The .NET/C# endpoint responds to POST with 200 if it receives a valid request body
ExampleController.cs
namespace ExampleProject.API.Controllers;
[ApiController, Route("[controller]/[action]")]
public class ExampleController : ControllerBase
{
// "/ExampleController/Post"
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Post([FromBody] Request request) => return Ok();
}
Request.cs
namespace ExampleProject.API.Models;
public class Request
{
// JSON must include this property with a non-empty string value.
[Required(AllowEmptyStrings = false)]
public string StringProperty { get; set; } = string.Empty;
// JSON must include this property with any int value.
[Required]
public int DintProperty { get; set; }
// JSON must include this property with 0 or more strings in an array.
[Required]
public IEnumerable<string> ArrayProperty { get; set; } = Enumerable.Empty<string>();
}
Working example with variable path
MAIN.TcPOU
Client : HttpClient;
Execute : BOOL := TRUE;
HasError : BOOL;
ErrorId : UDINT;
Client(
Execute := Execute,
Address := 'https://www.fake-example.com/ExampleController/Post',
CallMethod := 'POST',
Body := 'GVL.Body',
ResponseCode := 'GVL.ResponseCode', // This is 200 on success.
Response := '',
HasError => HasError,
ErrorId => ErrorId);
IF Execute THEN
Execute := FALSE;
END_IF
GVL.TcGVL
{attribute 'qualified_only'}
VAR_GLOBAL
Body : Body;
ResponseCode : INT;
END_VAR
Body.TcDUT
TYPE Body :
STRUCT
{attribute 'json' := 'stringProperty'}
StringProperty : STRING := 'string';
{attribute 'json' := 'dintProperty'}
DintProperty : INT := 1;
{attribute 'json' := 'arrayProperty'}
ArrayProperty : ARRAY[0..9] OF STRING := ['string'];
END_STRUCT
END_TYPE
header.json
{
"Accept": "*/*",
"Content-Type": "application/json"
}
Why do I care about using a JSON string if the variable path to a DUT is working ok?
In my working example above, the DUT contains a property of type ARRAY[0..9] OF STRING
, a static array, but the REST API endpoint takes in a dynamic array of strings, expected to vary with each call. For this use case, it is preferred to send:
{"stringProperty": "string", "dintProperty": 1, "arrayProperty": ["string"]}
instead of what the DUT deserializes to:
{"stringProperty": "string", "dintProperty": 1, "arrayProperty": ["string","","","","","","","","",""]}
Passing a JSON string to the HttpClient Body would be a workaround for all requests with dynamic arrays that do not serialize appropriately.