Handling Multiple Content Types (JSON, Form, Form-Data) in Spring MVC with Custom Annotations
This article explains how to make a Spring MVC endpoint simultaneously accept JSON, URL‑encoded form data, and multipart/form‑data by creating a custom @GamePHP annotation, implementing dedicated argument resolvers, and configuring the necessary beans to replace the default @RequestBody and @RequestParam handling.
In many legacy projects, a PHP API may accept both JSON and form‑data in the same endpoint, which can cause compatibility issues when migrating to Java Spring, where each controller method typically handles a single content‑type using @RequestBody for JSON and @RequestParam for form data.
A straightforward but clumsy approach is to inject the raw HttpServletRequest and manually parse the request body based on the Content‑Type header. The sample method
private Map<String, Object> getParams(HttpServletRequest request)demonstrates this, but it results in messy code, limited return types, and inability to use @Valid for parameter validation.
The article proposes an elegant solution: define a custom annotation @GamePHP and implement two argument resolvers— GameJsonMethodArgumentResolver and GameFormMethodArgumentResolver —to handle JSON and form submissions respectively. These resolvers are registered via a custom WebMvcConfigurer implementation.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GamePHP {} public class GamePHPMethodProcessor implements HandlerMethodArgumentResolver {
private GameFormMethodArgumentResolver formResolver;
private GameJsonMethodArgumentResolver jsonResolver;
// constructor initializes resolvers
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(GamePHP.class) != null;
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
String contentType = nativeWebRequest.getNativeRequest(ServletRequest.class).getContentType();
if (contentType == null) throw new IllegalArgumentException("Unsupported contentType");
if (contentType.contains("application/json")) {
return jsonResolver.resolveArgument(methodParameter, modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
}
if (contentType.contains("application/x-www-form-urlencoded")) {
return formResolver.resolveArgument(methodParameter, modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
}
if (contentType.contains("multipart")) {
return formResolver.resolveArgument(methodParameter, modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
}
throw new IllegalArgumentException("Unsupported contentType");
}
}The resolvers are added to Spring's argument resolver chain through a configuration class:
@Bean
public MyMvcConfigurer mvcConfigurer() {
return new MyMvcConfigurer();
}
public static class MyMvcConfigurer implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new GamePHPMethodProcessor());
}
}For multipart handling, the article suggests adding the commons-fileupload and commons-io dependencies and defining a MultipartResolver bean:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency> @Bean(name = "multipartResolver")
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setDefaultEncoding("UTF-8");
resolver.setResolveLazily(true);
resolver.setMaxInMemorySize(40960);
resolver.setMaxUploadSize(50 * 1024 * 1024);
return resolver;
}Finally, by replacing the standard @RequestParam annotation with the custom @GamePHP on controller parameters, the endpoint can transparently accept JSON, URL‑encoded form, and multipart/form‑data without additional boilerplate.
The article concludes with a tongue‑in‑cheek remark about PHP and promotional calls to join a discussion group, but the core technical content provides a reusable pattern for handling multiple content types in Spring MVC.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
