Response Wrapping
Though serializing bare models and arrays of models works fine for many APIs, Seltzr also supports wrapping responses in another object so that additional information can be included with the response.
Example Usage
For example, take the following API and sample response:
app.UseSeltzr<MyModel>(api => {
api.CanGet();
});
GET https://localhost:5000/
[
{ ... },
{ ... },
{ ... }
]
This API can be wrapped with the built-in BasicResponse<TModel> class to include information like version number and result count:
app.UseSeltzr<MyModel>(api => {
api
.WrapResponses()
.WriteResponseCountValue()
.WriteVersionValue("0.1.0")
.CanGet();
});
GET https://localhost:5000/
{
"Elements": [
{ ... },
{ ... },
{ ... }
],
"Count": 3,
"Version": "0.1.0"
}
The Response<TModel> Class
The Response<TModel> class is the base class for all wrapped responses. It includes utility methods for setting response values like Set
and SetString
, and a method for populating the response with the dataset. A subclass of Response<TModel> can include many properties, but only the ones that have been Set
will be serialized by a Result Writer. Additionally, a subclass should include a property of type TModel
, TModel[]
, or both, where TModel
is the type of the model class used for the API. The WrapResponse() method will use the BasicResponse<TModel> class to wrap responses, but the WrapResponse<TResponse>() overload can be used to wrap responses in any custom class derived from Response<TModel>.
Setting Response Values
Any public read-write property in a class derived from Response<TModel> can be marked as a response value by adding an attribute to it. Several are included with Seltzr, but any attribute that can be used on a property works. The ResponseValueAttribute can also be used to define custom properties without creating new attribute classes. The TModel
properties need not be marked with attributes.
Example
public class MyResponse : Response<MyModel> {
[ApiVersion]
public string? ApiVersion { get; set; }
[ResponseValue("CustomData")]
public int? CustomData { get; set; }
public TModel[]? Elements { get; set; }
}
Tip
You can find the source code of the BasicResponse<TModel> class here.
Setting Values with SeltzrOptionsBuilder<TModel, TUser>
Many builder methods are available for adding Post-Operation Actions that set response values.
- Deprecate(String)
- WriteResponseCountValue()
- WriteResponseValue(String, Func<IApiContext<TModel, TUser>, IEnumerable<TModel>, Object>)
- WriteResponseValue(String, Object)
- WriteResponseValue<TAttribute>(Func<IApiContext<TModel, TUser>, IEnumerable<TModel>, Object>)
- WriteResponseValue<TAttribute>(Object)
- WriteResponseValueAsync(String, Func<IApiContext<TModel, TUser>, IEnumerable<TModel>, Task<Object>>)
- WriteResponseValueAsync<TAttribute>(Func<IApiContext<TModel, TUser>, IEnumerable<TModel>, Task<Object>>)
- WriteVersion(String)
- WriteVersionValue(String)
Note
The WriteVersion(String) method will also set the X-Api-Version
header on responses.
Some of them, like Deprecate(String) and WriteResponseCountValue() use built-in attributes, but the WriteResponseValue
methods work with any attribute or ResponseValueAttribute value. Using the above response, we can set both ApiVersion
and CustomData
in the configuration method.
app.UseSeltzr<MyModel>(api => {
api
.WrapResponses<MyResponse>()
.WriteVersionValue("1.0")
.WriteResponseValue("CustomData", 5)
.CanGet();
});
Setting Values with IApiContext<TModel, TUser>
The Response<TModel> for the current request is available on the Response property of the IApiContext<TModel, TUser>. If the request is not wrapped, this property will be null
. Using the above example, we can set both ApiVersion
and CustomData
in a filter.
app.UseSeltzr<MyModel>(api => {
api
.WrapResponses<MyResponse>()
.Filter((ctx, d) => {
ctx.Response.Set<ApiVersionAttribute>("1.0");
ctx.Response.SetString("CustomData", "5");
return MyFilter(d);
})
.CanGet();
});
Tip
The SetString(String, String) and SetString<TAttribute>(String) methods will attempt to parse the given string to the correct type before setting it. In the above example, "5"
is parsed to the integer value 5
.