Spring Boot Cheatsheet

Controllers

Use this Spring Boot reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Controller Stereotypes

AnnotationReturnsUse When
@ControllerView name (Thymeleaf/Mustache)Server-side HTML rendering
@RestControllerJSON/XML body directlyREST APIs
@RestController = @Controller + @ResponseBody
@RestController
@RequestMapping("/api/users")
public class UserController {
    // all handler methods return body, no @ResponseBody needed
}

Basic CRUD Controller

@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor   // Lombok — generates constructor injection
public class ProductController {

    private final ProductService productService;

    @GetMapping
    public List<ProductDto> getAll() {
        return productService.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<ProductDto> getById(@PathVariable Long id) {
        return ResponseEntity.ok(productService.findById(id));
    }

    @PostMapping
    public ResponseEntity<ProductDto> create(@Valid @RequestBody CreateProductRequest req) {
        ProductDto created = productService.create(req);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
            .path("/{id}").buildAndExpand(created.id()).toUri();
        return ResponseEntity.created(location).body(created);
    }

    @PutMapping("/{id}")
    public ProductDto update(@PathVariable Long id,
                             @Valid @RequestBody UpdateProductRequest req) {
        return productService.update(id, req);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        productService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

Handler Method Parameter Annotations

AnnotationSourceExample
@PathVariableURL segment {id}@PathVariable Long id
@RequestParamQuery string ?page=0@RequestParam(defaultValue="0") int page
@RequestBodyJSON request body@RequestBody @Valid CreateDto dto
@RequestHeaderHTTP header@RequestHeader("X-API-Key") String key
@CookieValueCookie@CookieValue("session") String token
@ModelAttributeForm / multipart@ModelAttribute UserForm form
@MatrixVariableMatrix params /users/42;role=admin@MatrixVariable String role
HttpServletRequestRaw requestinjected automatically
PrincipalAuth principalPrincipal principal
@AuthenticationPrincipalSpring Security user@AuthenticationPrincipal UserDetails u
@SessionAttributeHTTP session@SessionAttribute("cart") Cart cart

@RequestParam Options

// Optional with default
@GetMapping
public Page<Item> list(
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "20") int size,
    @RequestParam(required = false) String search
) { ... }

// Multi-value: ?tag=java&tag=spring
@GetMapping("/search")
public List<Post> byTags(@RequestParam List<String> tag) { ... }

@PathVariable Options

// Name must match {variable} or use name attribute
@GetMapping("/users/{userId}/orders/{orderId}")
public Order get(@PathVariable Long userId, @PathVariable Long orderId) { ... }

// Optional path variable
@GetMapping({"/items", "/items/{id}"})
public Object getOrList(@PathVariable(required = false) Long id) { ... }

ResponseEntity Quick Reference

ResponseEntity.ok(body)                          // 200
ResponseEntity.created(uri).body(body)           // 201 + Location header
ResponseEntity.accepted().build()                // 202, no body
ResponseEntity.noContent().build()               // 204
ResponseEntity.badRequest().body(errorDto)       // 400
ResponseEntity.notFound().build()                // 404
ResponseEntity.status(HttpStatus.CONFLICT).body(msg) // 409

// Custom headers
return ResponseEntity.ok()
    .header("X-Custom", "value")
    .contentType(MediaType.APPLICATION_JSON)
    .body(data);

View Controllers (Thymeleaf / MVC)

@Controller
@RequestMapping("/")
public class PageController {

    @GetMapping("login")
    public String loginPage() {
        return "login";  // resolves to templates/login.html
    }

    @GetMapping("dashboard")
    public String dashboard(Model model) {
        model.addAttribute("user", currentUser());
        return "dashboard";
    }

    @PostMapping("register")
    public String register(@Valid @ModelAttribute RegisterForm form,
                           BindingResult result,
                           RedirectAttributes attrs) {
        if (result.hasErrors()) return "register";
        userService.register(form);
        attrs.addFlashAttribute("message", "Account created!");
        return "redirect:/login";
    }
}

Async Controllers

// Return CompletableFuture for servlet-thread offloading
@GetMapping("/report")
public CompletableFuture<Report> generateReport() {
    return CompletableFuture.supplyAsync(() -> reportService.build());
}

// Reactive (WebFlux only)
@GetMapping("/stream")
public Flux<Event> streamEvents() {
    return eventService.stream();
}

File Upload / Download

// Upload
@PostMapping("/upload")
public ResponseEntity<String> upload(@RequestParam MultipartFile file) throws IOException {
    String url = storageService.store(file.getOriginalFilename(), file.getInputStream());
    return ResponseEntity.ok(url);
}

// Download
@GetMapping("/files/{name}")
public ResponseEntity<Resource> download(@PathVariable String name) throws IOException {
    Resource resource = storageService.load(name);
    return ResponseEntity.ok()
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
        .contentType(MediaType.APPLICATION_OCTET_STREAM)
        .body(resource);
}

Set spring.servlet.multipart.max-file-size=10MB and spring.servlet.multipart.max-request-size=10MB in application.properties.

Common Pitfalls

  • Circular dependency — avoid field injection (@Autowired on field); use constructor injection so Spring fails loudly.
  • @PathVariable name mismatch — the variable name in the URL template must exactly match the parameter name (or specify name).
  • @RequestBody + @Valid@Valid must come before @RequestBody to trigger validation; missing it silently skips constraints.
  • HttpMediaTypeNotSupportedException — client must send Content-Type: application/json for @RequestBody endpoints.
  • Returning null vs Optional — return ResponseEntity.notFound().build() instead of null to get a proper 404.