Leveraging REST Messaging with Solace Schema Registry: Serializing and Deserializing Messages in .NET
You can use REST messaging with the SERDES Collection to handle structured message payloads efficiently over HTTP. The SERDES Collection provides .NET libraries for efficient serialization and deserialization of structured message payloads, allowing applications to convert complex data structures into a compact, transmittable format when publishing messages and to reconstruct them when consuming messages. Schema information is transmitted through HTTP headers rather than being embedded in the message payload, enabling efficient schema resolution and cross-protocol compatibility. The .NET SERDES libraries currently support JSON Schema serialization.
- Prerequisites
- Deploying with NuGet
- Required Using Statements
- Using SERDES with REST Messaging
- REST Consumer Application Parameters
- Serializing and Deserializing Messages with JSON Schema over REST
- Troubleshooting Cross-Protocol SERDES
Prerequisites
Before implementing SERDES with REST messaging, you should understand the language-agnostic concepts and configuration options covered in Serialization and Deserialization with Solace Schema Registry. Key topics include:
- Connecting to Schema Registry (authentication and security)
- Choosing schema resolution strategies (Destination ID, Topic ID, Topic ID with Profiles, Record ID)
- Optimizing performance (caching and lookup options)
- Advanced configuration (auto-registration, header formats, Avro and JSON Schema-specific settings)
- Cross-protocol message translation
This page focuses on REST-specific implementation details for .NET, including imports, HTTP client configuration, and code examples for serializing and deserializing JSON Schema messages over HTTP.
Deploying with NuGet
To use the SERDES Collection with your REST applications, you need to include the appropriate NuGet packages in your project. The SERDES libraries are distributed as separate packages that you can include based on your serialization format requirements.
The following sections show you how to configure your .NET project to include the necessary dependencies for schema registry integration:
JSON Schema Serializer Dependencies
The JSON Schema serializer provides a specific implementation for JSON Schema serialization and deserialization. This dependency includes the JsonSchemaSerializer<T>, JsonSchemaDeserializer<T>, and JSON Schema-specific configuration properties, and also brings in the required common SERDES components used across all schema formats.
Add the following package reference to your .NET project file:
<ItemGroup>
<PackageReference Include="Solace.SchemaRegistry.Serdes.JsonSchema" Version="1.0.0" />
</ItemGroup>
For the latest version information and additional details about the JSON Schema SERDES package, see the NuGet Gallery.
Required Using Statements
To use the JSON Schema SERDES with REST messaging, include the following namespaces in your .NET application:
using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Solace.SchemaRegistry.Serdes.JsonSchema; using Solace.SchemaRegistry.Serdes.Core;
Using SERDES with REST Messaging
REST messaging provides a lightweight, HTTP-based approach to message publishing and consumption that integrates with the SERDES Collection. When using SERDES with REST messaging, schema information is transmitted through HTTP headers rather than being embedded in the message payload, enabling efficient schema resolution and cross-protocol compatibility with AMQP and Solace Messaging API applications.
General REST Concepts with SERDES
When implementing SERDES with REST messaging, there are several important considerations:
- Header Format—REST messaging requires string-based headers, making the
SchemaIdStringheader format optimal for interoperability. For more information, see SERDES Header Configuration. This ensures schema identifiers are transmitted as human-readable strings in HTTP headers. - Content-Type and Message Type Handling—The HTTP Content-Type header determines how Solace interprets the message payload and the resulting message type for Solace .NET API consumers:
- Use
application/octet-streamfor binary content that results inBytesMessageobjects - Use
application/jsonand other text-based Content-Types for content that results inTextMessageobjects - The .NET SERDES deserializer operates on binary payloads regardless of Content-Type
- Use
- User Properties—SERDES headers are transmitted as Solace user properties using the
Solace-User-Property-prefix in HTTP headers (case-insensitive). - REST Delivery Point Configuration—REST consumer applications require proper configuration of a REST Delivery Point (RDP) on the Solace broker to receive messages. The RDP must be configured with the correct POST request target that matches your consumer application's endpoint. For detailed instructions, refer to Configuring REST Delivery Points.
.NET HttpClient and HttpListener Implementation Considerations
When using .NET's HttpClient for publishing and HttpListener for consuming REST messages with SERDES, there are additional specific considerations:
- HttpClient Best Practices—Use a single
HttpClientinstance throughout your application lifetime to avoid socket exhaustion. TheHttpClientclass is thread-safe and designed for reuse. - Header Naming—When adding SERDES headers to HTTP requests, use the
Solace-User-Property-prefix. Header names are case-insensitive in HTTP, but the prefix should match the broker's expected format. - HttpListener Platform Support—The
HttpListenerclass is available on Windows and provides a lightweight HTTP server for receiving messages. On Linux/macOS, consider using ASP.NET Core's Kestrel server for cross-platform compatibility. - Async Support—Both
HttpClientandHttpListenersupport asynchronous operations. The SERDES libraries also provide async methods (SerializeAsyncandDeserializeAsync) for non-blocking operations. - IDisposable Pattern—Both
JsonSchemaSerializer<T>andJsonSchemaDeserializer<T>implementIDisposableand must be properly disposed when no longer needed. Always use ausingstatement or explicitly callDispose()to ensure that resources are released and cached data is cleaned up.
Cross-Protocol Message Flow with SERDES
REST messaging integrates seamlessly with other protocols through SERDES. For detailed information about how REST messages flow to and from AMQP and Solace Messaging API consumers, including complete message flow examples, see Cross-Protocol Message Flow Examples.
Key REST-specific considerations for cross-protocol messaging with .NET:
- REST uses the
Solace-User-Property-prefix for SERDES headers in HTTP requests and responses. - Configure serializers with
SchemaIdStringformat for optimal REST compatibility. - REST consumers require a configured REST Delivery Point (RDP) on the broker.
- The Content-Type header determines message type:
application/octet-stream→ BytesMessage,application/json→ TextMessage.
REST Consumer Application Parameters
When implementing REST consumer applications that use SERDES, you need to configure several key parameters to ensure proper message reception and deserialization:
- Post Request Target—The endpoint path that your consumer application listens on (for example,
/my-rest-endpoint). This must match the "POST Request Target" configured in your broker's REST Delivery Point (RDP). - Port—The port number for your local HTTP server (for example,
8080). This determines where your consumer application will listen for incoming HTTP requests from the broker. - HTTP Topic Header Key—(Optional) The name of a custom HTTP header that contains the message's topic information. This is only needed if your RDP is configured to add topic information to a specific header (for example,
X-Solace-Topic).
Example consumer application startup with these parameters:
// Consumer listening on port 8080, endpoint /my-rest-endpoint, with topic in X-Solace-Topic header
string postRequestTarget = "/my-rest-endpoint";
int port = 8080;
string topicHeaderKey = "X-Solace-Topic"; // Optional
// Start HTTP listener with these parameters
using (var listener = new HttpListener())
{
listener.Prefixes.Add($"http://+:{port}{postRequestTarget}/");
listener.Start();
// Handle incoming requests...
}
Serializing and Deserializing Messages with JSON Schema over REST
The following examples demonstrate how to implement JSON Schema serialization and deserialization in REST messaging applications using .NET's HttpClient for publishing and HttpListener for consuming.
Publishing JSON Schema Messages over REST (.NET Example)
When publishing JSON Schema messages over REST, you create a JsonSchemaSerializer<T>, configure it with Schema Registry connection details (see Getting Started with SERDES), and serialize message payloads before sending them via HTTP POST requests.
Both JsonSchemaSerializer<T> and JsonSchemaDeserializer<T> implement IDisposable and must be properly disposed when no longer needed. Always use a using statement or explicitly call Dispose() to ensure that resources are released and cached data is cleaned up.
Key configuration for JSON Schema REST publishing:
- Configure
SchemaIdStringfor optimal REST compatibility - Set appropriate Content-Type (BINARY or JSON)
- Format SERDES headers as Solace user properties with
Solace-User-Property-prefix
Example configuration for a JSON Schema REST publisher:
// Create and configure the JSON Schema serializer
using (var serializer = new JsonSchemaSerializer<User>())
{
var config = new Dictionary<string, object>
{
{ JsonSchemaPropertyKeys.RegistryUrl, "http://localhost:8081/apis/registry/v3" },
{ JsonSchemaPropertyKeys.AuthUsername, "sr-readonly" },
{ JsonSchemaPropertyKeys.AuthPassword, "roPassword" },
// Use SchemaIdString for REST compatibility (string-based headers)
{ SerdePropertyKeys.SchemaHeaderIdentifiers, SchemaHeaderId.SchemaIdString }
};
serializer.Configure(config);
// Use serializer for publishing...
}
For information about configuring SERDES properties, see Getting Started with SERDES and JSON Schema-Specific Configuration.
Example serialization and HTTP request:
// Create a User object to serialize
var user = new User
{
Name = "John Doe",
Id = "123",
Email = "support@solace.com"
};
// Serialize the object - this populates the headers dictionary with schema information
var headers = new Dictionary<string, object>();
byte[] payload = await serializer.SerializeAsync(topicName, user, headers);
// Create HTTP request
string url = $"http://{brokerHost}:{port}/TOPIC/{topicName}";
var request = new HttpRequestMessage(HttpMethod.Post, url);
// Add SERDES headers with Solace-User-Property- prefix
foreach (var kvp in headers)
{
string headerName = $"Solace-User-Property-{kvp.Key}";
string headerValue = kvp.Value?.ToString() ?? string.Empty;
request.Headers.Add(headerName, headerValue);
}
// Set Content-Type for binary or JSON payload
request.Content = new ByteArrayContent(payload);
if (contentType == "JSON")
{
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
else
{
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
}
// Send the HTTP request
HttpResponseMessage response = await httpClient.SendAsync(request);
Console.WriteLine($"Published message with status: {response.StatusCode}");
For a complete example, see RestJsonSchemaPublisher.cs on the Solace Samples Repository.
For a complete list of JSON Schema SERDES properties and methods, see the .NET JSON SERDES API Reference.
Consuming JSON Schema Messages over REST (.NET Example)
When consuming JSON Schema messages over REST, you create an HTTP server using HttpListener that receives POST requests, extracts SERDES headers from the HTTP headers, and uses a JsonSchemaDeserializer<T> to reconstruct the original message payload.
Exception Types
When deserializing messages, the DeserializeAsync method can throw the following exceptions:
JsonSchemaValidationException(fromSolace.Serdesnamespace)—Thrown when the message payload does not conform to the JSON Schema retrieved from the registry. This indicates a schema validation failure.SerializationException(fromSolace.Serdesnamespace)—Thrown when the message payload cannot be decoded or deserialized, such as when the payload is malformed or the schema cannot be resolved from the registry.
Key aspects of JSON Schema REST consumption:
- Extract SERDES headers from HTTP headers with
Solace-User-Property-prefix (case-insensitive). - Handle type conversion for schema IDs (string vs. long) using regex pattern matching.
- Use appropriate HTTP status codes for different error conditions.
Example SERDES header extraction:
// Extracts SERDES headers and handles both string and typed values (e.g., "123 ; type=int64")
private static Dictionary<string, object> ExtractSerdesHeaders(NameValueCollection headers)
{
const string solaceUserPropertyPrefix = "solace-user-property-";
var serdesHeaders = new Dictionary<string, object>();
// Regex pattern to extract value and optional type: "<value> [; type=<type>]"
var typePattern = new Regex(@"(.*?)(?:\s*;\s*type=(\S+))?$");
foreach (string key in headers.AllKeys)
{
if (key == null) continue;
// Only process Solace user property headers (case-insensitive)
if (key.StartsWith(solaceUserPropertyPrefix, StringComparison.OrdinalIgnoreCase))
{
string httpValue = headers[key];
Match matcher = typePattern.Match(httpValue);
if (matcher.Success)
{
string valueAsString = matcher.Groups[1].Value.Trim();
string type = matcher.Groups[2].Success ? matcher.Groups[2].Value : null;
object value;
if (type != null && "int64".Equals(type.Trim(), StringComparison.OrdinalIgnoreCase))
{
// Convert to long for int64 type
value = long.TryParse(valueAsString, out long longValue) ? longValue : valueAsString;
}
else
{
// Default to string for SchemaIdString and other properties
value = valueAsString;
}
// Remove the solace-user-property- prefix to get original key
string propertyKey = key.Substring(solaceUserPropertyPrefix.Length).ToLowerInvariant();
serdesHeaders[propertyKey] = value;
}
}
}
return serdesHeaders;
}
Example deserialization and error handling:
// Extract SERDES headers from HTTP request
var serdesHeaders = ExtractSerdesHeaders(context.Request.Headers);
// Read message payload
byte[] messagePayload;
using (var ms = new MemoryStream())
{
await context.Request.InputStream.CopyToAsync(ms);
messagePayload = ms.ToArray();
}
// Get topic from optional header if configured
string requestTopic = "";
if (!string.IsNullOrEmpty(httpTopicHeaderKey))
{
requestTopic = context.Request.Headers[httpTopicHeaderKey] ?? "";
}
try
{
// Deserialize the message
User user = await deserializer.DeserializeAsync(requestTopic, messagePayload, serdesHeaders);
Console.WriteLine($"Received SERDES message: {user}");
// Send success response
await SendResponseAsync(context, 200, Array.Empty<byte>());
}
catch (JsonSchemaValidationException ve)
{
// Schema validation failed - return 422 Unprocessable Entity
Console.WriteLine($"Schema validation error: {ve.Message}");
await SendResponseAsync(context, 422, Encoding.UTF8.GetBytes(ve.Message));
}
catch (SerializationException se)
{
// General serialization/deserialization error - return 400 Bad Request
Console.WriteLine($"Serialization error: {se.Message}");
await SendResponseAsync(context, 400, Encoding.UTF8.GetBytes(se.Message));
}
catch (Exception e)
{
// Other unexpected errors - return 500 Internal Server Error
Console.WriteLine($"Unexpected error: {e.Message}");
await SendResponseAsync(context, 500, Encoding.UTF8.GetBytes("An internal server error occurred."));
}
For a complete example, see RestJsonSchemaConsumer.cs on the Solace Samples Repository.
For a complete list of JSON Schema SERDES properties and methods, see the .NET JSON SERDES API Reference.
Troubleshooting Cross-Protocol SERDES
When working with mixed REST, AMQP, and Solace .NET API environments using SERDES, you may encounter specific issues related to message type handling, HTTP headers, and cross-protocol compatibility. This section provides guidance for common troubleshooting scenarios.
Common Issues and Solutions
- Missing SERDES Headers—Verify that SERDES headers are being set with the
Solace-User-Property-prefix (case-insensitive). Check that headers are being extracted properly fromHttpListenerContext.Request.Headerson the consumer side. - Header Case Sensitivity—While HTTP headers are case-insensitive, ensure consistent casing when adding and extracting headers. The .NET samples use case-insensitive comparison (
StringComparison.OrdinalIgnoreCase) when checking for theSolace-User-Property-prefix. - Type Conversion Issues—When extracting SERDES headers, handle type suffixes properly (e.g.,
"; type=int64"). Use regex pattern matching to parse header values and convert to appropriate .NET types (string, long, etc.). - Content-Type Mismatch Issues—Ensure that REST publishers use appropriate Content-Type headers:
- Use
application/jsonfor JSON Schema messages that should be delivered as aTextMessage. - Use
application/octet-streamfor binary messages that should be delivered as aBytesMessage. - Verify that consumers can handle the expected message type.
- Use