Passing complex objects in the Query String​
ServiceStack uses the JSV-Format (JSON without quotes) to parse QueryStrings.
JSV lets you embed deep object graphs in QueryString as seen this example url:
https://test.servicestack.net/json/reply/StoreLogs?Loggers=[{Id:786,Devices:[{Id:5955,Type:Panel,
Channels:[{Name:Temperature,Value:58},{Name:Status,Value:On}]},
{Id:5956,Type:Tank,TimeStamp:1199303309,
Channels:[{Name:Volume,Value:10035},{Name:Status,Value:Full}]}]}]
If you want to change the default binding ServiceStack uses, you can register your own Custom Request Binder.
Custom Media Types​
ServiceStack serializes and deserializes your DTOs automatically. If you want to override the default serializers or you want to add a new format, you have to register your own Content-Type
:
Register a custom format​
string contentType = "application/yourformat"; //To override JSON eg, write "application/json"
var serialize = (IRequest req, object response, Stream stream) => ...;
var deserialize = (Type type, Stream stream) => ...;
//In AppHost Configure method
//Pass two delegates for serialization and deserialization
this.ContentTypes.Register(contentType, serialize, deserialize);
Encapsulate inside a plugin​
If you're looking to standardize on a custom implementation, it's recommended to wrap the registration inside a plugin.
E.g. here's how you can change ServiceStack to use .NET's XmlSerializer
instead of its DataContractSerializer
default:
public class XmlSerializerFormat : IPlugin
{
public static void Serialize(IRequest req, object response, Stream stream)
{
var serializer = new XmlSerializer(response.GetType());
serializer.Serialize(stream, response);
}
public static object Deserialize(Type type, Stream stream)
{
var serializer = new XmlSerializer(type.GetType());
var obj = (Type) serializer.Deserialize(stream);
return obj;
}
public void Register(IAppHost appHost)
{
appHost.ContentTypes.Register(MimeTypes.Xml, Serialize, Deserialize);
}
}
Where it can then be easily registered as a regular plugin:
Plugins.Add(new XmlSerializerFormat());
Other ContentType Examples​
The Protobuf-format shows an example of registering a new format whilst the Northwind VCard Format shows an example of creating a custom media type in ServiceStack.
For reference see registration examples of ServiceStack's different Formats:
Async ContentTypes Formats​
The async registration APIs are for Content-Type Formats which perform Async I/O, most serialization formats don't except for HTML View Engines which can perform Async I/O when rendering views, which are all registered using the RegisterAsync
APIs:
appHost.ContentTypes.RegisterAsync(MimeTypes.Html, SerializeToStreamAsync, null);
appHost.ContentTypes.RegisterAsync(MimeTypes.JsonReport, SerializeToStreamAsync, null);
appHost.ContentTypes.RegisterAsync(MimeTypes.MarkdownText, SerializeToStreamAsync, null);
Reading in and De-Serializing ad-hoc custom requests​
There are 2 ways to deserialize your own custom format, via attaching a custom request binder for a particular service or marking your service with IRequiresRequestStream
which will skip auto-deserialization and inject the ASP.NET Request stream instead.
Create a custom request dto binder​
You can register custom binders in your AppHost by using the example below:
appHost.RegisterRequestBinder<MyRequest>(httpReq => ... requestDto); // or:
appHost.RequestBinders.Add(typeof(MyRequest), httpReq => ... requestDto);
This gives you access to the IHttpRequest object letting you parse it manually so you can construct and return the strong-typed Request DTO manually which will be passed to the service instead.
Uploading Files​
You can access uploaded files independently of the Request DTO using Request.Files
. e.g:
public object Post(MyFileUpload request)
{
if (this.Request.Files.Length > 0)
{
var uploadedFile = base.Request.Files[0];
uploadedFile.SaveTo(MyUploadsDirPath.CombineWith(file.FileName));
}
return HttpResult.Redirect("/");
}
ServiceStack's imgur.netcore.io example shows how to access the byte stream of multiple uploaded files, e.g:
public object Post(Upload request)
{
foreach (var uploadedFile in base.Request.Files
.Where(uploadedFile => uploadedFile.ContentLength > 0))
{
using (var ms = new MemoryStream())
{
uploadedFile.WriteTo(ms);
WriteImage(ms);
}
}
return HttpResult.Redirect("/");
}
Reading directly from the Request Stream​
Instead of registering a custom binder you can skip the serialization of the Request DTO, you can add the IRequiresRequestStream interface to directly retrieve the stream without populating the Request DTO.
//Request DTO
public class RawBytes : IRequiresRequestStream
{
/// <summary>
/// The raw Http Request Input Stream
/// </summary>
Stream RequestStream { get; set; }
}
Which tells ServiceStack to skip trying to deserialize the request so you can read in the raw HTTP Request body yourself, e.g:
public async Task<object> PostAsync(RawBytes request)
{
byte[] bytes = await request.RequestStream.ReadFullyAsync();
string text = bytes.FromUtf8Bytes(); //if text was sent
}
Buffering the Request and Response Streams​
ServiceStack's Request and Response stream are non-buffered (i.e. forward-only) by default. This can be changed at runtime using a PreRequestFilters
to allow the Request Body and Response Output stream to be re-read multiple times should your Services need it:
appHost.PreRequestFilters.Add((httpReq, httpRes) => {
httpReq.UseBufferedStream = true; // Buffer Request Input
httpRes.UseBufferedStream = true; // Buffer Response Output
});
Which you'll then be able to re-read the Request Input Stream with:
string textBody = await httpReq.GetRawBodyAsync(); //read as string
ReadOnlySpan<byte> bytes = ((MemoryStream)httpReq.InputStream).GetBufferAsSpan(); //read as bytes
Raw SOAP Message​
You can access raw WCF Message when accessed with the SOAP endpoints in your Service with IHttpRequest.GetSoapMessage()
extension method, e.g:
Message requestMsg = base.Request.GetSoapMessage();
To tell ServiceStack to skip Deserializing the SOAP request entirely, add the IRequiresSoapMessage
interface to your Request DTO, e.g:
public class RawWcfMessage : IRequiresSoapMessage {
public Message Message { get; set; }
}
public object Post(RawWcfMessage request) {
request.Message... //Raw WCF SOAP Message
}