
Posted on • Originally published atwildermuth.com on
How to Access Query Strings in Minimal APIs
I love that this job allows me to learn new stuff every day. In this case, I was building a simple API to use for some upcomingPluralsight courses. I wanted to use Minimal APIs to expose some data for an old dataset fromFiveThirtyEight on Bechdel Tests for Films. While I was adding paging, I got confused.
So, I started with a minimal API for getting all the films like so:
app.MapGet("api/films", async (BechdelDataService ds) =>{ if (ds is null) return Results.BadRequest(); FilmResult data = await ds.LoadAllFilmsAsync(); if (data.Results is null) return Results.NotFound(); return Results.Ok(data);}).WithTags("By Film").Produces(200).ProducesProblem(404);
Pretty simple Minimal API to get some data (my BechdelDataService is just a repository-like class). When I added paging, I wanted to use the typical Web API trick of using optional parameters:
app.MapGet("api/films", async (BechdelDataService ds, int page = 1, int pageSize = 50) =>{...}).WithTags("By Film").Produces(200).ProducesProblem(404);
To my surprise this didn’t work. What I expected was that if I used a query string, it would bind to the extra parameters by name:
/api/films?page=1/api/films
In this case, these should be the same. What is going on. At first I dove into seeing why Minimal APIs didn’t support this. I didn’t see much about it, I saw options to read the query strings manually:
app.MapGet("api/films", async ( HttpRequest request, BechdelDataService ds) =>{ var page = request.Query["page"] ?? 1; var size = request.Query["pageSize"] ?? 50;...}).WithTags("By Film").Produces(200).ProducesProblem(404);
This works, but I found it clunky. I wanted binding to work. I saw some examples that broke out the lambda to a method:
app.MapGet("api/films", GetAllFilms);async Task<IResult> GetAllFilms(BechdelDataService ds, int page = 1, int pageSize = 50){ ...}
This works, so the problem wasn’t with Minimal APIs, the problem is that Lambdas do not support default values (since you’re passing in a lambda, the spec assumes that you’ll supply all values).
So this is a fix? I don’t like the messiness of breaking it out into separate methods. So how do we fix it? Let’s use nullable values:
app.MapGet("api/films", async (BechdelDataService ds, int? page, int? pageSize) =>{ if (ds is null) return Results.BadRequest(); int pageNumber = page ?? 1; int pagerTake = pageSize ?? 50; FilmResult data = await ds.LoadAllFilmsAsync(pageNumber, pagerTake); if (data.Results is null) return Results.NotFound(); return Results.Ok(data);}).WithTags("By Film").Produces(200).ProducesProblem(404);
By supplying the values as nullable types (int?), we have to do our own defaulting, but I think it’s acceptable. One note here, is that I’m defining the types to non-nullable types to make my calls to the data service easier, but this isn’t necessary.
So, was I the only one who didn’t know that default values weren’t supported in lambda’s?
This work byShawn Wildermuth is licensed under aCreative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
Based on a work atwildermuth.com.
If you liked this article, see Shawn's courses onPluralsight.
Top comments(1)
For further actions, you may consider blocking this person and/orreporting abuse