Careeroza
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:
| Annotation | HTTP method | Typical use in ShopEase | Success status |
|---|---|---|---|
@GetMapping | GET | List products, get one product | 200 OK |
@PostMapping | POST | Create a product | 201 Created |
@PutMapping | PUT | Replace a whole product | 200 OK |
@PatchMapping | PATCH | Change some fields (e.g. stock) | 200 OK |
@DeleteMapping | DELETE | Delete a product | 204 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 responseExample
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