My API sends requests to another system to get data. I am trying to give more control to the consumer of my API over the requests sent to the uderlying system, in the sense of filtering and specifying characteristics of the request.
I am hoping to achieve this by allowing the user to supply a specified JSON object in a header parameter.
Not all actions require this however, so I am using an attribute and an operationfilter to add the parameter where necessary.
The attribute is defined as below:
[AttributeUsage(AttributeTargets.Method)]
public class DmsFilter : Attribute
{
// Nothing here
}
The subsequent OperationFilter applied is as below:
public class DmsFilterOperationFilter : IOperationFilter
{
/// <inheritdoc />
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if(context.MethodInfo.GetCustomAttribute(typeof(DmsFilter), true) is null) return;
operation.Parameters ??= new List<OpenApiParameter>();
OpenApiSchema dmsFilterScheme =
context.SchemaGenerator.GenerateSchema(typeof(DmsRequestModel), context.SchemaRepository);
operation.Parameters.Add(new OpenApiParameter
{
Name = "dmsFilter",
Description = "Configure requests to Indicium here.",
In = ParameterLocation.Header,
Required = false,
Schema = dmsFilterScheme
});
}
private OpenApiSchema CreateDmsFilterScheme()
{
PropertyInfo[] propertyList = typeof(DmsRequestModel).GetProperties();
OpenApiSchema schema = new()
{
Type = nameof(DmsRequestModel),
Default = new OpenApiObject()
};
foreach (PropertyInfo propertyInfo in propertyList)
{
bool isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
if (schema.Properties.ContainsKey(propertyInfo.Name)) continue;
schema.Properties.Add(propertyInfo.Name, new OpenApiSchema
{
Type = propertyInfo.PropertyType.Name,
Default = isNullable ? null : new OpenApiString(string.Empty)
});
if (!isNullable)
schema.Required.Add(propertyInfo.Name);
}
return schema;
}
}
The object which I want to pass as JSON in a header, DmsRequestModel
, is as below:
public class DmsRequestModel
{
public string? Filter { get; set; }
public string? Select { get; set; }
public string? Prefilter { get; set; }
public string? OrderBy { get; set; }
public bool? IncludeFiles { get; set; }
public bool? IncludeHashCodes { get; set; }
}
I have tried using both fthe built-in SchemaGenerator to generate the object, as well as the self-defined CreateDmsFilterScheme
method in the OperationFilter. The resulting parameter from the SchemeGenerator appears to come closest to my needs so, to avoid confusion, the remainder of this question will be about this method.
In SwaggerUI, the resulting parameter looks as such:
On the surface, this appears to fit my needs exactly, as it looks like a valid JSON object.
However, when making an actual request, in the cURL output the entire object is seemingly supplied as a comma-separated string:
curl -X GET "http://localhost:5001/modules/dms/WorkOrders/all?api-version=1.0" -H "accept: text/plain" -H "basicAuthHeader: Basic <basic auth token>" -H "dmsFilter: prefilter,authorized,my_workshop,work_order_status,hide_work_order_status_cancelled,orderBy,planned_starting_date_time asc,includeFiles,false,includeHashCodes,true" -H "Authorization: Bearer <bearer token>"
I am not sure why this is happening, but I cannot seem to get the object into the header as a JSON string. I am not very experienced or familiar with HTTP technicalities, however I imagine supplying a JSON string in a header would not or should not be impossible. I have built APIs before but never with this level of complexity.
Am I doing this wrong, or is there some extra step somewhere which I am missing? I am not sure if this is the right way to go about this, but it felt like the most straight-forward and obvious way. Any suggestions or possible alternative ways of achieving this are welcome!
Please feel free to comment should anything be unclear.
Thank you in advance!