Managing and submitting form data, @ModelAttribute, form validation with BindingResult, ViewResolvers and their types, InternalResourceViewResolver, ContentNegotiatingViewResolver, HandlerInterceptor methods, global interceptors and interceptor vs filter.
Published September 25, 2026
Form and view questions come up for server-rendered applications (Thymeleaf or JSP). Interceptor questions come up everywhere. For forms, the pattern to know by heart is PRG: POST, validate, then Redirect to a GET.
Short answer: Bind the form fields to a form-backing object with @ModelAttribute. Spring's data binder matches request parameters to properties by name, and converts their types. Use @RequestParam for one-off fields.
public class SignupForm { // form-backing object
@NotBlank private String name;
@Email @NotBlank private String email;
@Size(min = 8) private String password;
// getters and setters (the binder uses them), or a record with a matching constructor
}
@GetMapping("/signup")
String showForm(Model model) {
model.addAttribute("signupForm", new SignupForm()); // empty object for the form
return "signup";
}
Learn it in depth → Bean Validation
Short answer: Use a @PostMapping method that takes the @Valid @ModelAttribute object and a BindingResult. If there are errors, redisplay the form. On success, process the data and redirect (Post/Redirect/Get), so that refreshing the page doesn't resubmit the form.
@PostMapping("/signup")
String submit(@Valid @ModelAttribute SignupForm form, BindingResult errors, RedirectAttributes flash) {
if (errors.hasErrors()) return "signup"; // same view, with the field errors
userService.register(form);
flash.addFlashAttribute("message", "Welcome aboard!");
return "redirect:/dashboard"; // PRG: avoids duplicate submissions
}
Common trap: BindingResult must come immediately after the object it validates. Otherwise Spring throws a MethodArgumentNotValidException / BindException instead of giving you the errors.
@ModelAttribute for?Short answer: It has two roles:
@ModelAttribute("countries")
List<String> countries() { return List.of("India", "Singapore", "Germany"); } // available to every view of this controller
Short answer: Annotate the form class with Bean Validation constraints (@NotBlank, @Email, @Size, @Pattern, and custom ones), and trigger validation with @Valid (or @Validated for validation groups). Errors land in the BindingResult, and the view shows them next to each field (in Thymeleaf, th:errors="*{email}").
Key points to cover:
Validator.Learn it in depth → Bean Validation
ViewResolver, and how does it work?Short answer: A ViewResolver turns the logical view name a controller returns ("order-details") into an actual View object that renders the output. Controllers therefore never hard-code template paths or technologies. The DispatcherServlet asks each configured resolver in turn (by order) until one returns a view.
ViewResolver exist?Short answer:
InternalResourceViewResolver: JSPs.ThymeleafViewResolver: Thymeleaf. This is Spring Boot's usual choice.FreeMarkerViewResolver: FreeMarker.BeanNameViewResolver: a view defined as a bean whose name matches the view name.ContentNegotiatingViewResolver: delegates to other resolvers based on the requested media type.MappingJackson2JsonView can render the model as JSON.Key points to cover:
XmlViewResolver and ResourceBundleViewResolver were deprecated and removed in Spring 6.InternalResourceViewResolver work?Short answer: It builds a path from prefix + view name + suffix, and forwards the request to that JSP inside the web application.
@Bean
InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver r = new InternalResourceViewResolver();
r.setPrefix("/WEB-INF/views/"); // under WEB-INF, so JSPs can't be requested directly
r.setSuffix(".jsp");
return r; // "home" → /WEB-INF/views/home.jsp
}
Key points to cover:
ContentNegotiatingViewResolver?Short answer: One controller method can serve several formats: HTML for browsers, JSON or XML for API clients. The resolver chooses the view from the requested media type (the Accept header, or a path extension or parameter if configured), and delegates to the matching view resolvers and default views.
Key points to cover:
@RestController endpoints, with produces and message converters, are the more common way to serve JSON.Short answer: A HandlerInterceptor runs code before and after the handler executes. It's used for cross-cutting web concerns that need to know which handler runs: request timing, audit logging, tenant resolution, locale or theme changes, simple API-key checks, and adding common model attributes.
public class RequestTimingInterceptor implements HandlerInterceptor {
@Override public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
req.setAttribute("startNanos", System.nanoTime());
return true; // false = stop the chain (you write the response)
}
@Override public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
long ms = (System.nanoTime() - (long) req.getAttribute("startNanos")) / 1_000_000;
log.info("{} {} took {} ms (status {})", req.getMethod(), req.getRequestURI(), ms, res.getStatus());
}
}
HandlerInterceptor interface have?Short answer:
preHandle: runs before the handler. Returning false stops processing.postHandle: runs after the handler, before the view renders. It can modify the ModelAndView. It's not called if the handler threw an exception, and for @ResponseBody methods the response has already been written.afterCompletion: runs after the request completes, even on exceptions (it receives the exception). Use it for cleanup and timing.Key points to cover:
default methods since Spring 5, so you override only the ones you need.Short answer: Register it in a WebMvcConfigurer. Without path patterns, it applies to every handler. Narrow it with addPathPatterns and excludePathPatterns.
@Configuration
class WebConfig implements WebMvcConfigurer {
@Override public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestTimingInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/actuator/**", "/static/**");
}
}
Short answer:
| Servlet filter | Spring MVC interceptor | |
|---|---|---|
| Layer | Servlet container, before the DispatcherServlet | Inside Spring MVC, around handler execution |
| Applies to | Every request (static resources, other servlets) | Requests dispatched to Spring handlers |
| Knows the handler? | No | Yes (method, annotations) |
| Can replace request/response objects | Yes (wrap them) | No |
| Typical uses | Security (Spring Security), CORS, compression, encoding, request logging | Timing, auditing, handler-specific checks, model enrichment |
Q: What is a flash attribute?
A: A value stored temporarily (in the session) so that it survives one redirect, typically a success message after a PRG redirect. It's added with RedirectAttributes.addFlashAttribute.
Q: How do you protect forms against CSRF? A: Spring Security's CSRF protection (on by default for session-based apps) requires a token in each state-changing request. Thymeleaf adds it to forms automatically. Stateless token-authenticated APIs usually disable CSRF.
Q: How do you bind a date field from a form?
A: Annotate the field with @DateTimeFormat(iso = ISO.DATE), or register a Formatter or Converter. java.time types such as LocalDate are supported directly.
Q: Where would you put authentication checks: filter or interceptor? A: In the filter chain, through Spring Security, so that every entry point is protected before any MVC processing happens. Interceptors are fine for finer, handler-aware checks on top of that.