Create a Custom HttpMessageConverter in Spring Boot 2.6
This article demonstrates how to create a custom HttpMessageConverter in Spring Boot 2.6.12 to parse a simple “name:张三,age:20” string into a Users object, configure it in WebMvc, and explains the underlying Spring MVC request‑handling flow that invokes the converter.
Environment: Spring Boot 2.6.12
Goal: implement a custom message format where the request body is a simple string like "name:张三,age:20".
Custom HttpMessageConverter
<code>public class CustomHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
private static Logger logger = LoggerFactory.getLogger(CustomHttpMessageConverter.class);
// Only support Users type
@Override
protected boolean supports(Class<?> clazz) {
return Users.class == clazz;
}
// Read and convert request body
@Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
String content = inToString(inputMessage.getBody());
String[] keys = content.split(",");
Users instance = null;
try {
instance = (Users) clazz.newInstance();
} catch (Exception e1) {
e1.printStackTrace();
}
for (String key : keys) {
String[] vk = key.split(":");
try {
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
if (f.getName().equals(vk[0])) {
f.setAccessible(true);
Class<?> type = f.getType();
if (String.class == type) {
f.set(instance, vk[1]);
} else if (Integer.class == type) {
f.set(instance, Integer.parseInt(vk[1]));
}
break;
}
}
} catch (Exception e) {
logger.error("错误:{}", e);
}
}
return instance;
}
// Write response using toString()
@Override
protected void writeInternal(Object t, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
outputMessage.getBody().write(t.toString().getBytes());
}
@Override
protected boolean canWrite(MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) {
return true;
}
}
return false;
}
private String inToString(InputStream is) {
byte[] buf = new byte[10 * 1024];
int leng = -1;
StringBuilder sb = new StringBuilder();
try {
while ((leng = is.read(buf)) != -1) {
sb.append(new String(buf, 0, leng));
}
return sb.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
</code>Configure the Converter
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
CustomHttpMessageConverter messageConvert = new CustomHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(new MediaType("application", "fm"));
messageConvert.setSupportedMediaTypes(supportedMediaTypes);
converters.add(messageConvert);
WebMvcConfigurer.super.configureMessageConverters(converters);
}
}
</code>When the converter is registered, the client must set Content-Type: application/fm for the request.
Parameter Object
<code>public class Users {
private String name;
private Integer age;
@Override
public String toString() {
return "【name = " + this.name + ", age = " + this.age + "】";
}
}
</code>Controller
<code>@RestController
@RequestMapping("/message")
public class MessageController {
@PostMapping("/save")
public Users save(@RequestBody Users user) {
System.out.println("接受到内容:" + user);
return user;
}
}
</code>Test request and response (images omitted) show that the custom format is correctly parsed and echoed.
Why the Converter Overrides These Methods
The @RequestBody annotation triggers RequestResponseBodyMethodProcessor , which eventually delegates to AbstractMessageConverterMethodArgumentResolver . The processing flow is:
DispatcherServlet → RequestMappingHandlerAdapter → invokeHandlerMethod → ServletInvocableHandlerMethod → invokeForRequest → getMethodArgumentValues → readWithMessageConverters.
During readWithMessageConverters the framework iterates over registered HttpMessageConverter instances. Our CustomHttpMessageConverter is selected because canRead returns true for the Users class and the application/fm media type.
The selected converter’s readInternal parses the raw string into a Users object, and writeInternal serializes the object back to the custom string format.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.