Request Mappings and HTTP Methods

Mapping URLs to Methods

In the basic level you wrote one @GetMapping. A real API maps many URLs and HTTP methods to Java methods. Spring MVC gives you one annotation per HTTP method:

AnnotationHTTP methodTypical use in ShopEaseSuccess status
@GetMappingGETList products, get one product200 OK
@PostMappingPOSTCreate a product201 Created
@PutMappingPUTReplace a whole product200 OK
@PatchMappingPATCHChange some fields (e.g. stock)200 OK
@DeleteMappingDELETEDelete a product204 No Content

@RequestMapping on the class sets a common prefix, so every method below only writes the rest of the path.

A Controller Skeleton

package com.shopease.product;

import org.springframework.web.bind.annotation.*;

@RestController                      // @Controller + @ResponseBody: return values become JSON
@RequestMapping("/api/products")     // prefix for every method in this class
public class ProductController {

    @GetMapping                      // GET    /api/products
    public String list() { return "all products"; }

    @GetMapping("/{id}")             // GET    /api/products/7
    public String get(@PathVariable Long id) { return "product " + id; }

    @PostMapping                     // POST   /api/products
    public String create() { return "created"; }

    @PutMapping("/{id}")             // PUT    /api/products/7
    public String replace(@PathVariable Long id) { return "replaced " + id; }

    @PatchMapping("/{id}")           // PATCH  /api/products/7
    public String patch(@PathVariable Long id) { return "patched " + id; }

    @DeleteMapping("/{id}")          // DELETE /api/products/7
    public String delete(@PathVariable Long id) { return "deleted " + id; }
}

Note: @GetMapping("/x") is short for @RequestMapping(path = "/x", method = RequestMethod.GET). Use the short forms; they are easier to read.

How a Request Finds Your Method

curl GET /api/products/7?fields=name
        |
        v
Embedded Tomcat (port 8080)
        |
        v
DispatcherServlet  --->  HandlerMapping: which method matches "GET /api/products/{id}"?
        |
        v
ProductController.get(id = 7)   (arguments resolved from path, query, body)
        |
        v
HttpMessageConverter (Jackson)  --->  Java object  ->  JSON response

Example

The idea behind HandlerMapping in plain Java: match the method and the path pattern, pull out {id} and query parameters, then call the handler. If nothing matches you get a 404 (if the path matches but the method does not, Spring actually returns 405).

import java.util.*;
import java.util.function.Function;

public class Main {
    record Route(String method, String pattern, Function<Map<String, String>, String> handler) {}

    static final List<Route> routes = new ArrayList<>();

    static void map(String method, String pattern, Function<Map<String, String>, String> handler) {
        routes.add(new Route(method, pattern, handler));
    }

    static String dispatch(String method, String url) {
        int q = url.indexOf('?');
        String path = q < 0 ? url : url.substring(0, q);
        Map<String, String> query = new LinkedHashMap<>();
        if (q >= 0) {
            for (String pair : url.substring(q + 1).split("&")) {
                String[] kv = pair.split("=");
                query.put(kv[0], kv[1]);
            }
        }
        for (Route r : routes) {
            if (!r.method().equals(method)) continue;
            String[] want = r.pattern().split("/");
            String[] got = path.split("/");
            if (want.length != got.length) continue;
            Map<String, String> vars = new LinkedHashMap<>(query);
            boolean ok = true;
            for (int i = 0; i < want.length; i++) {
                if (want[i].startsWith("{")) vars.put(want[i].substring(1, want[i].length() - 1), got[i]);
                else if (!want[i].equals(got[i])) { ok = false; break; }
            }
            if (ok) return "200 " + r.handler().apply(vars);
        }
        return "404 No handler for " + method + " " + path;
    }

    public static void main(String[] args) {
        map("GET", "/api/products", v -> "list products, category=" + v.getOrDefault("category", "all"));
        map("GET", "/api/products/{id}", v -> "get product id=" + v.get("id"));
        map("DELETE", "/api/products/{id}", v -> "delete product id=" + v.get("id"));

        System.out.println(dispatch("GET", "/api/products"));
        System.out.println(dispatch("GET", "/api/products?category=mobiles"));
        System.out.println(dispatch("GET", "/api/products/7"));
        System.out.println(dispatch("DELETE", "/api/products/7"));
        System.out.println(dispatch("POST", "/api/orders"));
    }
}

Output

200 list products, category=all
200 list products, category=mobiles
200 get product id=7
200 delete product id=7
404 No handler for POST /api/orders