Coder Social home page Coder Social logo

Comments (1)

captainsafia avatar captainsafia commented on June 15, 2024

Content pulled from https://blog.safia.rocks/minimal-apis-optionality.html

In .NET 6 RC1, we shipped support for a new feature in Minimal APIs that allows developers to set the optionality of request parameters by using nullable annotations and default parameters to indicate which values are required and which aren't. For example, let's say you had an endpoint that generated a random number based on a seed, like so:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/random", (int seed, int max) => 
{
	var random = new Random(seed);
	return random.Next(0, max);
});

app.Run();

By default, in the scenario above, the seed parameter will be treated as required. That means if the user sends the following request to the endpoint:

$ http "http://localhost:5184/random"               
HTTP/1.1 400 Bad Request
Content-Length: 0
Date: Sun, 22 Aug 2021 21:52:51 GMT
Server: Kestrel

They'll be meet with a 400 Bad Request response. However, everything is all fine and dandy if both values are provided, though.

$ http "http://localhost:5184/random?seed=5&max=100"
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Aug 2021 22:06:56 GMT
Server: Kestrel
Transfer-Encoding: chunked

33

As it turns out, we can generate a Random object without providing a seed. By annotating seed as a nullable property, we can permit users to send requests to the endpoint without providing a seed property to the query.

app.MapGet("/random", (int? seed, int max) => 
{
	var random = seed.HasValue ? new Random(seed.Value) : new Random();
	return random.Next(0, max);
});

In this case, both requests below are valid.

$ http "http://localhost:5184/random?max=100" 
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Aug 2021 22:08:16 GMT
Server: Kestrel
Transfer-Encoding: chunked

17

$ http "http://localhost:5184/random?seed=5&max=100"
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Aug 2021 22:08:35 GMT
Server: Kestrel
Transfer-Encoding: chunked

33

However, since the max attribute is still required, omitting that from the request will result in the 400 Bad Request error.

$ http "http://localhost:5184/random?seed=5"
HTTP/1.1 400 Bad Request
Content-Length: 0
Date: Sun, 22 Aug 2021 22:09:22 GMT
Server: Kestrel

In addition to nullable annotations, we can indicate that a parameter is optional by specifying a default value for the parameter.

int GetRandom(int? seed, int max = 5)
{
    var random = seed.HasValue ? new Random(seed.Value) : new Random();
    return random.Next(0, max);
}
app.MapGet("/random", GetRandom);

⚠️ Note: In the code above, the endpoint logic has been moved to a separate function since default parameters in inline lambdas are not currently supported in C#.

The change above permits the user to provide the seed and max parameters as optional within requests. All the following requests will be processed by the endpoint.

$ http "http://localhost:5184/random?seed=5&max=100"
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Aug 2021 22:12:58 GMT
Server: Kestrel
Transfer-Encoding: chunked

33

$ http "http://localhost:5184/random?seed=5"        
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Aug 2021 22:15:11 GMT
Server: Kestrel
Transfer-Encoding: chunked

1

$ http "http://localhost:5184/random"       
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Aug 2021 22:15:24 GMT
Server: Kestrel
Transfer-Encoding: chunked

4

$ http "http://localhost:5184/random?max=100" 
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 22 Aug 2021 22:15:51 GMT
Server: Kestrel
Transfer-Encoding: chunked

95

In the above scenario, we were able to mark a query parameter as optional but the same principles apply to parameters in the body as well as services injected to the endpoint. So for example, let's say that we took the configuration for our random number generator as a set of arguments provided in the body of the request.

app.MapPost("/random", (ConfigOptions? options) =>
{
    var random = options is not null ? new Random(options.Seed) : new Random();
    return random.Next(0, options?.Max ?? 100);
});

app.Run();

class ConfigOptions
{
    public int Seed { get; }
    public int Max { get; }
}

The above will appropriately handle requests where the the body parameters are provided and those where it isn't.

One thing to note is that the behavior is different for service parameter. For one, instead of returning a 400 status code, the endpoint will return a 500 status code depending on whether or not the service was provided. Also, due to some nuances in the parameter parsing logic, optionality is only support for services that are injected into the endpoint via an explicit reference (using the FromService attribute) and not those that are implicit referenced.

🗒️ The nuance here is that there is some subtlety around discerning whether the SomeType in app.MapPost("/foo", (SomeType st) => ...) is referring to a service SomeType or a body param that deserializes to SomeType.

Finally, depending on the nullability context the parameter exists in the, the behavior of this feature will differ slightly.

  • Unannotated value types are always required regardless of nullability context.
  • Unannotated reference types are always optional if they exist in an unknown nullability context.
  • Regardless of nullability context, you can use default values to indicate that a reference or value type parameter is optional.

One final note, by default, no message will be sent in the response when a parameter fails a requiredness check. For more information on this, including current solutions, check out this GitHub issue.

from minimal-apis.github.io.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.