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
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);
}
}
}Configure the Converter
@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);
}
}When the converter is registered, the client must set Content-Type: application/fm for the request.
Parameter Object
public class Users {
private String name;
private Integer age;
@Override
public String toString() {
return "【name = " + this.name + ", age = " + this.age + "】";
}
}Controller
@RestController
@RequestMapping("/message")
public class MessageController {
@PostMapping("/save")
public Users save(@RequestBody Users user) {
System.out.println("接受到内容:" + user);
return user;
}
}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.
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.
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.
