This contains some thoughts how to implement RESTful APIs.

Background

tbd

Best practices

A search for RESTful API best practices reveals many sites:

Resources

Use plural nouns to describe resources

Verbs

Taken from Hevodata and Microsoft Guidelines

REST VerbActionIs Idempotent
GETfetch one or more values from the resourcetrue
POSTcreate a new resource (or set of resources)false
PUTreplace an existing object entirely or created a new one, if ID does not existtrue
DELETEdelete an existing objecttrue
 these are used less frequently
PATCHmodify a field of an existing resourcefalse
OPTIONget information about a requesttrue

The first four verbs (GET, POST, PUT, DELETE) respond to the the CRUD operation of persistent storage.

Questions:

  • PUT - how should it behave if the ID does not exist?
  • POST - does it create the ID?

POST

Return 201 Created and should return the created location

GET

tbd

PATCH

tbd

Minimal API

Minimal APIs are a simplified approach for building fast HTTP APIs with ASP.NET Core. You can build fully functioning REST endpoints with minimal code and configuration. Skip traditional scaffolding and avoid unnecessary controllers by fluently declaring API routes and actions.

For now, we show how to implement a file upload in F# (see also Minimal API F# for more examples).

To create a Minimal API program, use:

% dotnet new web --lang f# -o TodoApi

This is the whole program:

[<EntryPoint>]
let main args =
    let builder = WebApplication.CreateBuilder(args)
    let app = builder.Build()

    app.MapPost("/upload", Func<IFormFile, Task<IResult>>(fun file -> task {
        let tempFile = IO.Path.Join(IO.Path.GetTempPath(), file.FileName)
        app.Logger.LogInformation("tempFile", tempFile, "filename", file.FileName)
        use stream = IO.File.OpenWrite(tempFile)
        file.CopyTo(stream)
        stream.Close()
        return Results.Ok("uploaded " + file.FileName)
    })) |> ignore

    app.Run()

    0 // Exit code

It reads the posted file and copies the file to a local temporary file. A little text message is displayed in the browser. In a real example, you would parse the stream and do something useful with it.

The corresponding HMTL looks like this:

<form method="post" action="/upload" enctype="multipart/form-data">
    <label>
        Upload file:
        <input type="file" name="file"/>
    </label>
    <button type="submit">Upload the file</button>
</form>

The code looks much simpler than the corresponding MVC controller. It can also be tested using CURL:

% curl --form file='@filename' http://localhost:PORT/upload

Minimal API and HTML content

Minimal API is aimed at RESTful APIs, not necessarily at HTML pages. To return a HTML page, use:

app.MapGet("/", Func<Task<IResult>>(fun () -> task {
        let page = IO.File.ReadAllText("index.html")
        return Results.Content(page, contentType = "text/html")
    })) |> ignore

This example can also be found in my github.

Miscellaneous

Simple bulma index.html

Often I look for the simplest index.html possible. I use this one with bulma CSS framework:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
  </head>
  <body>
  <section class="section">
    <div class="content">
      <h1>De finibus bonorum et malorum</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>
    </div>
  </section>
  </body>
</html>

References

Fielding, Roy Thomas (2000). “Chapter 5: Representational State Transfer (REST)”. Architectural Styles and the Design of Network-based Software Architectures (Ph.D.). University of California, Irvine.