Reputation: 38499
I'm trying to save semi arbitrary json to CosmosDb
I say semi because the start of the json will have some known properties. For example:
{
"id": "88e37e40-fd7e-478e-9ac5-140601fcddad",
"type": "http",
"name": "request1",
"body": {
"property1" : 1,
"otherProperty": "foo"
}
}
As you can see, body
is the arbitrary part.
id
and type
and name
are for demo purposes, but we could need to validate these properties.
Here's what I'm trying
app.MapPost
(
"/", async (HttpRequest httpRequest, Container db) =>
{
var postedRequest = await httpRequest
.ReadFromJsonAsync<Request>();
var id = postedRequest.id;
var type = postedRequest.type;
var savedItem = await db.CreateItemAsync(postedRequest, new PartitionKey(type));
return Results.Created($"/requests/{type}/{id}", savedItem.Resource);
}
);
public record Request(string id, string type, string name, dynamic body);
My problem is when I POST the example json at the top of this question, it's being persisted like so:
{
"id": "88e37e40-fd7e-478e-9ac5-140601fcddad",
"type": "http",
"name": "request1"
"body": {
"ValueKind": 1
},
"_rid": "WxgaAN+DhzoBAAAAAAAAAA==",
"_self": "dbs/WxgaAA==/colls/WxgaAN+Dhzo=/docs/WxgaAN+DhzoBAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-96ae-5ccf928801d8\"",
"_attachments": "attachments/",
"_ts": 1657712863
}
It's using ValueKind in place of the arbitrary json. I believe this is because of how the framework is deserializing it, perhaps due to my usage of dynamic
Is there another type is should use? Or perhaps deserialize HttpRequest body to a stream first, then probe the properties I am interested in validating them? Using Utf8JsonReader? I'm not sure where to go from here.
Here's my full code
using Microsoft.Azure.Cosmos;
using CosmosClient client = new("<connectionstring>");
Database database = await client.CreateDatabaseIfNotExistsAsync(
id: "testing"
);
Container container = await database.CreateContainerIfNotExistsAsync(
id: "requests",
partitionKeyPath: "/type",
throughput: 400
);
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<Container>(container);
var app = builder.Build();
app.MapGet("/", () => "Started");
app.MapPost
(
"/", async (HttpRequest httpRequest, Container db) =>
{
var postedRequest = await httpRequest
.ReadFromJsonAsync<Request>();
var id = postedRequest.id;
var type = postedRequest.type;
var savedItem = await db.CreateItemAsync(postedRequest, new PartitionKey(type));
return Results.Created($"/requests/{type}/{id}", savedItem.Resource);
}
);
app.MapGet
(
"/requests/{type}/{id}", (Container db, string type, string id) =>
{
var dbResult = db.ReadItemAsync<Request>(id, new PartitionKey(type));
return Results.Ok(dbResult);
}
);
app.Run();
public record Request(string id, string type, string name, dynamic body);
Upvotes: 2
Views: 573
Reputation: 15583
If you can know the Partition Key (it comes in the url for example), then it would be simpler to use the Stream APIs. Example: https://github.com/ealsur/ondotnet-cosmosdb/tree/master/src/episode1/streams#streams
app.MapPost
(
"/{type}/{id}", async (string type, string id, HttpRequest httpRequest, Container db) =>
{
var savedItem = await db.CreateItemStreamAsync(httpRequest.Body, new PartitionKey(type));
return Results.Stream(savedItem.Content, "application/json");
}
);
Upvotes: 1