HTTP Verbs

Gürçağ Yaman
4 min readJan 2, 2024

--

RESTful APIs usually work over the architecture of HTTP/HTTPS protocols, communicate using URIs representing resources and HTTP verbs/methods such as GET, POST, PUT, PATCH and DELETE, respond as HTTP messages with HTTP Status Codes such as 2xx, 3xx, 4xx, 5xx.

Resource & URI

A Resource refers to an entity that is identified by a URI.

Some key characteristics of resources and URIs in terms of REST principles are below:

  • Stateless: The server doesn’t store any information about the client’s state between requests.
  • Representation: Resources have one or more representations such as JSON, XML, etc.
  • Supports HTTP Verbs: GET, POST, PUT, PATCH, DELETE etc.
  • Naming Convention: Plural nouns instead of verbs.
    For instance “/articles” instead of “/getarticles”.
  • Sub-Resources Paths: URIs should include paths that represent sub-resources, if there are.
    For instance “/articles/{id}/author/{id}”.

HTTP Verbs

Understanding the concepts of safety and idempotency is beneficial before beginning to define HTTP verbs.

  • Safe: An HTTP method is safe if it is a read-only operation and does not modify the server state. Defined at RFC.
  • Idempotent: The effect on the server state for the same request is the same result. In other words, an idempotent operation has no additional modification on the server side whether the same request with the same parameters is called once or more than once.
  HTTP Verb     | Safe     | Idempotent
---------------|----------|------------
GET | Yes | Yes
OPTIONS | Yes | Yes
POST | No | No
PUT | No | Yes
PATCH | No | Yes
DELETE | No | Yes

All safe methods are idempotent as well, but not all idempotent methods are safe.

HTTP Verbs are listed and defined at IANA.
My simple definitions and code blocks using ASP.NET Core Minimal API are below:

  • GET: In order to retrieve a representation of the specified resource.
app.MapGet("/products", async (ProductDbContext db) =>
Results.Ok(await db.Products.ToListAsync()));

app.MapGet("/products/{id}", async (long id, ProductDbContext db) =>
await db.Products.FindAsync(id)
is Product product
? Results.Ok(product)
: Results.NotFound());
  • OPTIONS: In order to retrieve which HTTP verbs are supported for the specified resource.
app.MapMethods("/products", new[] { "OPTIONS" }, (HttpContext context) =>
{
context.Response.Headers.AppendCommaSeparatedValues
("Allow", ["GET, POST, PUT, PATCH, DELETE, OPTIONS"]
);
return Results.NoContent();
});
  • POST: In order to create a new resource.
app.MapPost("/products", async (Product product, ProductDbContext db) =>
{
bool productExists = await db.Products.AnyAsync(x => x.Id == product.Id);

// HTTP 409 Error Response for the resource already exists
if (productExists) return Results.Conflict();

await db.Products.AddAsync(product);
await db.SaveChangesAsync();

return Results.Created($"/products/{product.Id}", product);
});
  • PUT: To modify the complete state of the specified resource, not just a subset of the resource.
app.MapPut("/products/{id}", async (long id, Product product, ProductDbContext db) =>
{
if (string.IsNullOrEmpty(product?.Name)
|| string.IsNullOrEmpty(product?.Description))
{
// HTTP 400 for the invalid request model
return Results.BadRequest();
}

var productEntity = await db.Products.FindAsync(id);

if (productEntity is null) return Results.NotFound();

productEntity.Name = product.Name;
productEntity.Description = product.Description;

await db.SaveChangesAsync();

return Results.NoContent();
});
  • PATCH: To make updates to specified parts of the specified resource with a single request. Expects a JsonPatchDocument which may contains one or more update operations such as replace, remove etc.
// An HTTP PATCH request body
[
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo" }
]
app.MapPatch("/products/{id}", async (long id, HttpContext httpContext, ProductDbContext db) =>
{
if (!httpContext.Request.HasJsonContentType())
{
Results.BadRequest();
}

JsonPatchDocument? patchDocument;

using (var streamReader = new StreamReader(httpContext.Request.Body))
{
var httpContent = await streamReader.ReadToEndAsync();
patchDocument = JsonConvert.DeserializeObject<JsonPatchDocument>(httpContent);
}

if (patchDocument is null) return Results.BadRequest();

var productEntity = await db.Products.FindAsync(id);

if (productEntity is null) return Results.NotFound();

patchDocument.ApplyTo(productEntity);

await db.SaveChangesAsync();

return Results.NoContent();
});

HTTP Status Codes

Amazon 404 Not Found Error Page

HTTP Response Status Codes can be divided into five groups: 1xx Informational, Successful 2xx, Redirection 3xx, Client Error 4xx and Server Error 5xx.

We should avoid using HTTP 200 Success messages with an in-body error model. Choosing a corresponding error status code with a clear and understandable error message is the correct approach.

All of HTTP Response Status Codes are defined and explained at RFC.

I developed an ASP.NET Core Minimal API application in order to perform and demonstrate what I have mentioned in this article.
Can be seen at My Github Repo.

--

--

Gürçağ Yaman

🎓B. Sc. in Mathematical Engineering @ YTU & 💻 Software Engineer