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
[Never])If you’re new to Popcorn, start with Getting Started first.
Sometimes a model carries fields the API client should never see — a database-internal row
version, a denormalized lookup key, a row-level secret. Popcorn gives you [Never]: a
compile-time guarantee that the marked field is dropped during serialization, no matter what
the client requests.
v7 → v8 note. This attribute was called
[InternalOnly]in v7. In v8 it’s[Never](and lives in thePopcornnamespace). Semantics are otherwise unchanged.
Say we add a Social Security Number to our Employee model. Clients should never receive it:
using Popcorn;
public class Employee
{
[Default] public string FirstName { get; set; } = "";
[Default] public string LastName { get; set; } = "";
[Never] public string SocialSecurityNumber { get; set; } = "";
}
The field is still a normal C# property — internal code can read and write it. It just gets dropped on the way out:
GET /employees?include=[FirstName,LastName,SocialSecurityNumber]
{
"Success": true,
"Data": [
{ "FirstName": "Liz", "LastName": "Lemon" },
{ "FirstName": "Jack", "LastName": "Donaghy" }
]
}
Note that the request asked for SocialSecurityNumber and the server simply omitted it — no
error, no special handling. [Never] is stronger than “not in the default set”; it beats every
other inclusion mechanism: ?include=[!all] won’t emit it, [SubPropertyDefault("[SSN]")]
on the parent won’t emit it, explicit ?include=[SocialSecurityNumber] won’t emit it.
[Never] vs omitting the field entirelyThe safest way to keep a field off the wire is simply not to have it on the model the API
returns. Two situations where [Never] is the better answer:
Employee class is also your
domain entity, you can’t drop SocialSecurityNumber — internal code needs it. [Never]
lets the property stay in code and be invisible to the API.[Never] to sensitive
fields on the source model gives you an extra guardrail — if someone refactors and
accidentally returns the domain entity directly, the field still stays off the wire.If your API surface is naturally a separate DTO class and the source model never crosses the
API boundary, you can skip [Never] — just omit the property from the DTO.
[Never] is better than “remove from projection”In v7 users would often strip sensitive fields by leaving them off the projection class. That
worked, but it was easy to undermine accidentally (blind expansion would walk the source
object, or a future refactor added a “quick” path that returned the entity directly). v8
preserves [Never] explicitly at compile time because it’s the one attribute whose guarantee
is load-bearing for security.
[SubPropertyDefault][Never] takes precedence over [SubPropertyDefault]. Even if a parent’s [SubPropertyDefault]
lists a [Never]-marked child property by name, the field stays hidden:
public class Employee
{
[SubPropertyDefault("[Make,Model,Color,InternalVin]")] // mentions InternalVin explicitly
public List<Car> Vehicles { get; set; } = new();
}
public class Car
{
public string Make { get; set; } = "";
public string Model { get; set; } = "";
public Colors Color { get; set; }
[Never] public string InternalVin { get; set; } = "";
}
GET /employees?include=[Vehicles] emits Make + Model + Color per car and no InternalVin.
The substitution list is used to pick the default set; each child property still consults its
own attribute stack before being emitted.
[Default] and [Always].?include=
grammar.