Using .NET SDK to create DynamicData object for Form schema

I have a React app that calls a .net core proxy API to retrieve data and (ideally) to create content as well. I would prefer not to strongly type each of my forms and would like to POST the JSON to a web service and have it create the content and return 200, 400, or 500 responses so the front-end can handle it accordingly.

I could write manual WebClient POST code for this, but would prefer to use the SDK. However, I can’t seem to find a good way to do this or at least a code sample for this scenario. Is it possible to create a Dynamic Data client that references a specific schema to create content for that schema?

Thanks for your advice/recommendations!

it depends what yo udo wit the content. If you don’ want to process on a field level you could have a look to Yarp: https://microsoft.github.io/reverse-proxy/articles/getting-started.html

If you want to process the content you could use the following pattern. It is pseudo code, but you should get the idea:

class ProxyController
{
	private readonly SquidexClientManager squidex;
	
	public ProxyController(SquidexClientManager squidex)
	{
		this.squidex = squidex;
	}
	
	[HttpPost("/api/content/{schema}")]
	public async Task<IActionResult> PostContent(string schema, [FromBody] DynamicData data,
		CancellationToken ct = default) 
	{
		try
		{
			var client = squidex.CreateDynamicContentsClient(schema);
			
			await client.CreateAsync(data, default, ct);
			return Ok();
		}
		catch (...)
		{
			return StatusCode(...);
		}
	}
}

DynamicData uses Newtonsoft.JSON.

Therefore you have to switch to NewtonsoftJson

services.AddMvc()
    .AddNewtonsoftJson();

I haven’t found a way to configure Newtonsoft.JSON on controller level.

There is a small sample for a Node proxy to implement caching:

Thanks @Sebastian! I started to go down the route you put together in pseudo code but wanted to confirm that was a viable direction. I saw intellisense that said I should limit the creation of new instances of the Contents Client (SquidexClientManager.CreateContentsClient). I suppose in my case (using this for form submissions) wouldn’t be frequent. All other clients are created via DI. I’ll let you know if this ends up working for my scenario.

Yes, this is a good point. But you can just cache them in a ConcurrentDictionary.

services.AddMvc().AddNewtonsoftJson();

This is not an option for me as I’m using a .net core 5 project. I can add NewtonsoftJson, but not the microsoft core namespace version to configure the service.

So, I’m trying to pass in [FromBody]dynamic, but get serialization errors when trying to deserialize that object into DynamicData. Any suggestions on the best way to Deserialize an object to DynamicData? This is not working - it doesn’t seem to serialize correctly. Again, I would prefer to pass in a dynamic so every form I create doesn’t need a model added to the codebase.

[HttpPost]
[Route("Create/{Schema}")]
public async Task<IActionResult> CreateContent(string Schema, [FromBody]dynamic Data)
{
    var serializedData = JsonConvert.DeserializeObject<DynamicData>(Data.ToString());

    var created = await _apiClient.CreateContentAsync(Schema, serializedData);
    if (created)
        return Ok(created);
    else
        return StatusCode(500);
}

public async Task<bool> CreateContentAsync(string Schema, DynamicData Data, CancellationToken ct = default)
{
      try
      {
          var client = _clientManager.CreateDynamicContentsClient(Schema);
          await client.CreateAsync(Data, new ContentCreateOptions() { Publish = false }, null, ct);
          return true;
      }
      catch (Exception ex) {
          return false;
      }
  }

The error is

`{"Squidex Request failed: {\"message\":\"Validation error\",\"traceId\":\"00-fd4bc330cf9e314aaf8c84ccd8178129-f17b2bf17e96ecca-00\",\"type\":\"https://tools.ietf.org/html/rfc7231#section-6.5.1\",\"details\":[\"Unexpected end when deserializing object. Path '', line 7, position 1.\"],\"statusCode\":400}"}`

Incoming JSON is pretty basic at this point.

{
    "firstName": "Bobby",
    "lastName": "Brown",
    "email": "bb@mailinator.com",
    "phone": "555-555-5555",
    "message": "This is my message!"
}

It is also available in .NET 5: https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.newtonsoftjsonmvcbuilderextensions.addnewtonsoftjson?view=aspnetcore-5.0

if this is not an option I would deserialize it manually:

// Pseudo Code
private static readonly JsonSerializer Serializer = JsonSerializer.CreateDefault();

public async Task<IActionResult> CreateContent(string Schema, [FromBody]dynamic Data)
{
   // Not sure if this overload with a stream exists.
    var serializedData = JsonSerializer.Deserialize<DynamicData>(HttpContext.Request.Body);

    var created = await _apiClient.CreateContentAsync(Schema, serializedData);
    if (created)
        return Ok(created);
    else
        return StatusCode(500);
}

Scroll down to see how to deserialize from a stream: https://www.newtonsoft.com/json/help/html/Performance.htm

But Squidex data must be in this format, so you have to convert it to:

{
  "firstName": {
     "iv": "Bobby"
  }
}

:man_facepalming: I tried to install the 6.0 version of Newtonsoft. Sorry, dev skills are a little rusty.

So the incoming simple JSON can’t be Deserialized into DynamicData to give it the iv format?

I was able to add Newtonsoft to services and pass in [FromBody]DynamicData and got it to work. If I can accomplish the deserialization into iv format it will make my React dev simpler.

It is a little bit complicated. The normal format in Squidex is like this:

{
  "firstName": {
     "iv": "Bobby"
  }
}

But to support the X-Flatten header (https://docs.squidex.io/02-documentation/concepts/localization#x-flatten-header) the DynamicData supports both formats.

But when you pass the flat version to Squidex it does not work, because Updates (POST, PUT, PATCH) always require the nested format. Therefore you have to convert it, e.g. like this:

// ToList() is important here, because you cannot modify data, while enumerating.
foreach (var (key, value) in data.ToList())
{
   data[key] = new JObject
  {
     ["iv"] = value
  };
}

Perhaps I will provide a sample tomorrow, how to write a proxy with .NET.

1 Like