Popcorn is a .Net Middleware for your RESTful API that allows your consumers to request exactly as much or as little as they need, with no effort from you.
This project is maintained by Skyward App Company
Popcorn’s ?include= grammar supports two wildcard keywords: !all and !default. Both are
prefixed with ! (not - — that prefix is reserved for negation).
v7 → v8 note. In v7 you could use
*as a wildcard (?include=[*]). In v8 the wildcard is!all. If you’re migrating a client, update the URLs.
!all — every available field!all includes every property on the target type (minus anything marked [Never], which still
wins). Useful for admin screens that need the full record without spelling out every field.
Model:
using Popcorn;
public class Employee
{
[Default] public string FirstName { get; set; } = "";
[Default] public string LastName { get; set; } = "";
public DateTimeOffset Birthday { get; set; }
public int VacationDays { get; set; }
[Never] public string SocialSecurityNumber { get; set; } = "";
public List<Car> Vehicles { get; set; } = new();
}
Bare request (returns default set):
GET /employees
→ { FirstName, LastName }
Wildcard request:
GET /employees?include=[!all]
{
"Success": true,
"Data": [
{
"FirstName": "Liz",
"LastName": "Lemon",
"Birthday": "1981-05-01T00:00:00+00:00",
"VacationDays": 0,
"Vehicles": [...]
},
{ "FirstName": "Jack", "LastName": "Donaghy", ... }
]
}
SocialSecurityNumber is still absent — [Never] beats !all. That’s the point: [Never]
is the one attribute whose guarantee is load-bearing for security.
!default — the configured default set!default is the default set explicitly invoked. It’s only interesting in combination with
other include tokens, since a bare request already yields the default set:
GET /employees?include=[!default,Birthday]
→ FirstName, LastName, and Birthday
Handy when you want “the default set plus one extra field” without respelling the defaults.
!all,-Field and !default,-FieldBoth wildcards combine with -FieldName to drop specific fields:
GET /employees?include=[!all,-Birthday]
→ everything except Birthday
GET /employees?include=[!default,-LastName]
→ default set without LastName
Negating an [Always]-marked field is a silent no-op — [Always] wins.
Wildcards also work inside nested brackets:
GET /employees?include=[FirstName,Vehicles[!all]]
{
"Success": true,
"Data": [
{
"FirstName": "Liz",
"Vehicles": [
{ "Make": "Pontiac", "Model": "Firebird", "Year": 1981, "Color": 2 }
]
},
...
]
}
This is particularly useful when the parent has a tight default set but you want to walk the full child shape.
Typos or fields that don’t exist on the target type are treated as no-ops:
GET /employees?include=[FirstName,Whoops]
→ { FirstName }
This is intentional — clients evolve at a different cadence from servers, and a 500 on a
renamed field breaks more things than it helps. Names must resolve to the wire name (the
[JsonPropertyName("…")] argument if present, otherwise the C#-name normalized by your
JsonNamingPolicy). See Include Parameter Syntax
for the full contract.
[Never] and why wildcards can’t override it.