How do you make a @RequestParam optional? Explain required, defaultValue, and Optional, and how they interact.
answer
- required=true is the default -> 400 when missing
- defaultValue implies not-required; it's a String, gets converted
- Optional<T> / nullable = not required
- primitive + required=false = boom; use wrapper
- ?q= is present (empty), defaultValue won't fire
basics
~20 sBy default @RequestParam is required, so a missing param -> 400. Make it optional with required=false (value is null when absent), or give defaultValue="..." (used when absent, and implies not-required), or declare the type as Optional<T> or a nullable type.
solid answer
~40 s@RequestParam and @RequestHeader/@CookieValue default to required=true, so a missing value throws (MissingServletRequestParameterException -> HTTP 400). Three ways to relax it: (1) required = false — the argument is null (or empty Optional) when absent; (2) defaultValue = "7" — supplies a fallback String that's then type-converted, and setting it automatically makes the param not required; (3) declare the parameter as java.util.Optional<T>, which Spring treats as required=false. Precedence: defaultValue always wins when the param is missing, so combining defaultValue with required=true is contradictory and the default still applies. Gotcha: with required=false on a primitive (int), an absent param would try to bind null to a primitive and fail — use a wrapper (Integer), Optional, or a defaultValue. Also, an empty string param (?q=) is *present* — defaultValue does NOT kick in, you get "".
code
java · 10 lines@GetMapping("/search")
public Page<Item> search(
@RequestParam String q, // required -> 400 if missing
@RequestParam(defaultValue = "0") int page, // absent -> 0 (implies not required)
@RequestParam(defaultValue = "20") int size, // String "20" converted to int
@RequestParam Optional<String> sort, // absent -> Optional.empty()
@RequestParam(required = false) Integer minPrice // wrapper, not primitive!
) {
return service.search(q, page, size, sort.orElse("name"), minPrice);
}go deeper
Knows required=false / defaultValue make a param optional.
Explains all three mechanisms and that defaultValue implies not-required.
Calls out the primitive trap and the present-but-empty vs absent distinction.
Discusses API-contract implications and prefers Optional for NPE-safety and explicit presence semantics.
**The default is required.** @RequestParam has a `required` attribute that defaults to `true`. If the client omits the parameter, Spring throws MissingServletRequestParameterException, which the default error handling maps to HTTP 400 Bad Request. The same required-by-default rule applies to @RequestHeader and @CookieValue (@PathVariable is effectively always required because a missing segment fails URL matching). **Three mechanisms to make it optional:** 1. **required = false** ```java @RequestParam(required = false) String q // q == null when absent ``` When the param is missing, the argument is bound to `null` (for reference types) or an empty `Optional` (if declared Optional). No exception. 2. **defaultValue** ```java @RequestParam(defaultValue = "0") int page // page == 0 when absent ``` defaultValue is a **String** that Spring type-converts to the target type. Crucially, **specifying defaultValue implies required = false** — you don't set both. The default is applied only when the parameter is *absent*. 3. **Optional<T>** ```java @RequestParam Optional<String> q // Optional.empty() when absent ``` Declaring the parameter as java.util.Optional makes Spring treat it as not-required; you get Optional.empty() rather than null. This is a clean, NPE-safe style. In Kotlin, a nullable type `String?` on @RequestParam is likewise treated as not required. **Precedence / interaction:** defaultValue takes priority when the value is missing. If you (contradictorily) write required = true together with a defaultValue, the defaultValue still fills in the missing value — effectively overriding the required flag. Practically: pick one of the three approaches per parameter. **Key gotchas:** - **Primitive + required=false is a trap.** `@RequestParam(required=false) int page` will attempt to bind `null` to a primitive int when absent and fail with an IllegalStateException/500. Use Integer, Optional<Integer>, or a defaultValue. - **Empty string is 'present', not 'absent'.** For `?q=`, the value is the empty string "", so defaultValue does NOT apply and required=true is satisfied. If you want blank-as-missing you must check it yourself. (Spring 5.3+ has some empty-handling nuances, but the safe mental model is: present-but-empty != absent.) - **defaultValue is always a String literal**, even for numbers/enums/dates — it is converted like a real incoming value, so `defaultValue = "2020-01-01"` works with a LocalDate parameter that has the right formatter. - The same required/defaultValue attributes exist on @RequestHeader and @CookieValue and behave identically. **When to use which:** use defaultValue for a sensible fallback (pagination page/size, sort order); use Optional<T> when 'not provided' is semantically meaningful and you want to branch on presence; use required=false + null in older/simple code, but prefer Optional to avoid NPEs.
- Why does @RequestParam(required=false) int page blow up at runtime when the param is absent?When absent, Spring binds null, but null can't be assigned to a primitive int, so it fails (typically a 500 / IllegalStateException). Fix it with Integer, Optional<Integer>, or a defaultValue that supplies a concrete value.
- If a request has ?q= (empty), does defaultValue apply?No. An empty value is still 'present', so the parameter is considered supplied and defaultValue only fires when the parameter is entirely absent. You'd receive the empty string and must handle blank yourself.
saying these in an interview costs you the question
- Saying you must set both required=false and defaultValue together (defaultValue already implies not-required).
- Believing defaultValue kicks in for an empty-string value.
- Using a primitive with required=false and expecting null/0 instead of a runtime error.