Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding Spring MVC Request Handling and Data Sharing

Tech May 14 2

MVC Architectural Pattern

MVC divides application logic into three interconnected components:

  • Model (M): Represents JavaBeans handling data. Two primary types exist:
    • Entity Beans: Store business data (e.g., Customer, Product).
    • Businses Logic Beans: Handle service and data access operations (e.g., Service, Repository).
  • View (V): Represents user interface components like HTML or JSP pages responsible for data presentation and user interaction.
  • Controller (C): Manages application flow, typically implemented as servlets, to receive requests and send responses.

Note: MVC's presentation layer aligns with the three-tier architecture's presentation layer, which includes UI components and backend servlets. Spring MVC provides abstraction over servlet handling.

Regarding url-pattern configurations in web.xml:

  • /* matches any request sent to the server.
  • / does not match requests ending with .jsp as JSP pages are compiled into servlets.

Core Concepts and Configuration

View Resolution: Physical view path = View prefix + Logical view name + View suffix.

DispatcherServlet Configuration in web.xml:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <!-- Specifies the location of the Spring MVC configuration file -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-web-config.xml</param-value>
    </init-param>
    <!-- Loads the servlet during server startup; lower numbers indicate higher priority -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Request Flow Summary: The DispatcherServlet, configured as the front controller, intercepts requests matching its url-pattern. It reads the Spring MVC configuration to locate controllers. The request URL is matched against the @RequestMapping annotation's value attribute on controller methods. Upon a successful match, the corresponding method executes, returning a logical view name. This name is resolved by the ViewResolver by adding configured prefix and suffix to locate the physical view template (e.g., JSP, Thymeleaf), which is then rendered and forwarded to the client.

The @RequestMapping Annotation

Placement

  • On a class: Defines a base request path for all handler methods within that controller.
  • On a method: Specifies the specific request path for that handler.
@Controller
@RequestMapping("/api/users")
public class UserController {
    // Handles requests to /api/users/profile
    @RequestMapping("/profile")
    public String showUserProfile() {
        return "user-profile-view";
    }
}

The value Attribute

Maps requests to handler methods based on the URL path. It accepts an array of Strings, allowing a single method to handle multiple paths.

@RequestMapping({"/details", "/info"})
public String getDetails() { ... }

The method Attribute

Filters requests based on HTTP methods (GET, POST, etc.). It uses the RequestMethod enum.

@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createItem() { ... }

Derived annotations offer shorthand for common methods: @GetMapping, @PostMapping, @PutMapping, @DeleteMapping.

Mismatch Consequence: A path match with a method mismatch results in HTTP 405 (Method Not Supported).

The params Attribute

Filters requests based on the presence and values of request parameters.

  • "paramName": Request must include paramName.
  • "!paramName": Request must not include paramName.
  • "paramName=expectedValue": Request must include paramName with the specified value.
  • "paramName!=excludedValue": Request may omit paramName, but if present, its value must not equal excludedValue.

Mismatch Consequence: Results in HTTP 400 (Bad Request).

The headers Attirbute

Filters requests based on HTTP header values, using syntax identical to the params attribute.

Mismatch Consequence: Results in HTTP 404 (Not Found) if the path and method match but headers do not. Note: Header keys are case-insensitive, while values are case-sensitive.

Advanced Path Mapping

Ant-style Patterns

  • ?: Matches any single character.
  • *: Matches zero or more characters within a path segment.
  • **: Matches zero or more directories across the path. Must be used as /** within the pattern.

URI Template Variables (Path Placeholders)

Enables RESTful URL patterns.

// Handles GET /products/delete/123
@GetMapping("/products/delete/{productId}")
public String removeProduct(@PathVariable("productId") Long id) {
    // `id` will be 123
    return "confirmation-view";
}

Accessing Request Parameters

1. Using Servlet API

Inject HttpServletRequest as a method parameter.

@PostMapping("/login")
public String processLogin(HttpServletRequest req) {
    String user = req.getParameter("username");
    String pass = req.getParameter("password");
    // ... process data
    return "result-view";
}

2. Direct Method Parameter Binding

Method parameter names must match request parameter names.

@PostMapping("/login")
public String directBinding(String username, String password) {
    System.out.println("User: " + username);
    return "result-view";
}

3. Using @RequestParam Annotation

Provides control over parameter binding.

  • value / name: The name of the request parameter.
  • required: Whether the parameter is mandatory (default: true).
  • defaultValue: A default value if the parameter is not supplied.
@GetMapping("/search")
public String searchItems(
        @RequestParam(value = "q", required = false, defaultValue = "") String query,
        @RequestParam("page") int pageNumber) {
    // ...
    return "search-results-view";
}

4. Binding Headers and Cookies

  • @RequestHeader: Binds an HTTP header value to a method parameter.
  • @CookieValue: Binds a cookie's value to a method parameter.

5. Binding to a POJO (Command Object)

If request parameter names match the properties of a plain Java object, Spring can automatically populate it.

public class UserForm {
    private String username;
    private String email;
    // getters and setters
}

@PostMapping("/register")
public String handleRegistration(UserForm newUser) {
    // `newUser` is populated with request parameters 'username' and 'email'
    System.out.println(newUser.getUsername());
    return "welcome-view";
}

Handling Character Encoding

To prevent garbled characters from request parameters, configure an encoding filter in web.xml. It must be the first filter in the chain.

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <!-- Forces response encoding as well -->
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Sharing Data Across Scopes

1. Request Scope

  • Using HttpServletRequest:
    request.setAttribute("dataKey", dataObject);
    
  • Using ModelAndView: Combines model data and view specification.
    @GetMapping("/data")
    public ModelAndView getData() {
        ModelAndView mav = new ModelAndView("data-view");
        mav.addObject("message", "Data loaded successfully.");
        return mav;
    }
    
  • Using Model, ModelMap, or Map<String, Object>: These types are interchangeable as method parameters for adding request attributes.
    @GetMapping("/info")
    public String getInfo(Model model) { // Can also use ModelMap or Map
        model.addAttribute("serverTime", LocalDateTime.now());
        return "info-page-view";
    }
    

2. Session Scope

Use HttpSession to store data across multiple requests from the same client.

@PostMapping("/cart/add")
public String addToCart(@RequestParam Long itemId, HttpSession session) {
    ShoppingCart cart = (ShoppingCart) session.getAttribute("userCart");
    if (cart == null) {
        cart = new ShoppingCart();
        session.setAttribute("userCart", cart);
    }
    cart.addItem(itemId);
    return "redirect:/cart/view";
}

3. Application Scope

Use ServletContext (available via HttpServletRequest.getServletContext()) to store global, application-wide data.

@GetMapping("/stats")
public String showStats(HttpServletRequest request) {
    ServletContext ctx = request.getServletContext();
    Integer visitCount = (Integer) ctx.getAttribute("totalVisits");
    // ...
    return "statistics-view";
}

Accessing Data in Views (e.g., Thymeleaf)

  • Request attribute: ${attributeName}
  • Session attribute: ${session.attributeName}
  • Application attribute: ${application.attributeName}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.