I have an web API written using C# on the top of ASP.NET 5/core with EntityFrameworkCore.
I am using OData to apply filters on when querying the database.
I need to manually apply the ODataQueryOptions
to my DbSet<>
to generate paged info.
Here is my code
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly DbContext _db;
public ProductsController(DbContext db)
{
_db = db;
}
[HttpGet]
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All, AllowedFunctions = AllowedFunctions.AllFunctions, MaxTop = 500)]
public PagedProduct Search(ODataQueryOptions<Product> queryOptions)
{
// create a query for that would count the total rows
var countQuery = queryOptions.ApplyTo(Repository.Query(), AllowedQueryOptions.Top | AllowedQueryOptions.Skip) as IQueryable<Product>;
// create a query to pull the data
var query = queryOptions.ApplyTo(Repository.Query()) as IQueryable<Product>;
var result = new PagedProduct()
{
CurrentPage = 1, // TODO get the current page value from the queryOptions
PageSize = 100, // TODO get the page size value from the queryOptions
};
result.TotalRecords = await countQuery.Count();
result.Data = await query.ToList();
return result;
}
}
But the above code cause Swagger UI to fail. I get the following error
Fetch error undefined /swagger/v1/swagger.json
That error is caused by using ODataQueryOptions
as parameter into the Search action.
How can I get the Swagger UI to work with ODataQueryOptions
?
I ran into similar problem few years ago, so maybe my solution will be useful to you.
I used "native" parameters instead of ODataQueryOptions in my controller:
[Route("", Name = "GetDocuments")]
[HttpGet]
[Produces(typeof(PageDto<DocumentShortDto>))]
public async Task<IActionResult> GetDocumentsAsync(
[FromQuery(Name = "$top")] int top,
[FromQuery(Name = "$skip")] int skip,
[FromQuery(Name = "$orderby")] string orderby,
[FromQuery(Name = "$filter")] string filter)
Then I created ODataQueryOptions with the help of the following class:
public static class ODataBuilder
{
public static ODataQueryOptions<T> BuildOptions<T>(HttpRequest request)
{
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.AddEntityType(typeof(T));
var edmModel = modelBuilder.GetEdmModel();
var oDataQueryContext = new ODataQueryContext(edmModel, typeof(T), new ODataPath());
return new ODataQueryOptions<T>(oDataQueryContext, request);
}
}
The advantage of this approach is that the user can now specify required parameters and run requests in Swagger.
If Swagger throws InvalidOperationException with the message "Add at least one media type to the list of supported media types", it means that some OData formatters have unsupported media types.
Since service uses only native parameters and doesn't respond with OData, you can safely remove all OData formatters at startup code and resolve this issue:
services.AddMvcCore(options =>
{
var inputFormattersToRemove = options.InputFormatters.OfType<ODataInputFormatter>().ToList();
foreach (var formatter in inputFormattersToRemove)
{
options.InputFormatters.Remove(formatter);
}
var outputFormattersToRemove = options.OutputFormatters.OfType<ODataOutputFormatter>().ToList();
foreach (var formatter in outputFormattersToRemove)
{
options.OutputFormatters.Remove(formatter);
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With