Spring MVC - Creating a custom HttpMessageConverter

[Updated: Sep 2, 2017, Created: Sep 2, 2017]

This example demonstrates how to create and register a custom HttpMessageConverter.

Creating the Custom Converter

Following custom converter converts the message body to the Report object. The converter expects that the first line of message body will be the report name and the rest will be the report content.

public class Report {
  private int id;
  private String reportName;
  private String content;
    .............
}
public class ReportConverter
      extends AbstractHttpMessageConverter<Report> {

  public ReportConverter() {
      super(new MediaType("text", "report"));
  }

  @Override
  protected boolean supports(Class<?> clazz) {
      return Report.class.isAssignableFrom(clazz);
  }

  @Override
  protected Report readInternal(Class<? extends Report> clazz, HttpInputMessage inputMessage)
          throws IOException, HttpMessageNotReadableException {
      String requestBody = toString(inputMessage.getBody());
      int i = requestBody.indexOf("\n");
      if (i == -1) {
          throw new HttpMessageNotReadableException("No first line found");
      }

      String reportName = requestBody.substring(0, i).trim();
      String content = requestBody.substring(i).trim();

      Report report = new Report();
      report.setReportName(reportName);
      report.setContent(content);
      return report;
  }

  @Override
  protected void writeInternal(Report report, HttpOutputMessage outputMessage)
          throws IOException, HttpMessageNotWritableException {
      try {
          OutputStream outputStream = outputMessage.getBody();
          String body = report.getReportName() + "\n" +
                  report.getContent();
          outputStream.write(body.getBytes());
          outputStream.close();
      } catch (Exception e) {
      }
  }

  private static String toString(InputStream inputStream) {
      Scanner scanner = new Scanner(inputStream, "UTF-8");
      return scanner.useDelimiter("\\A").next();
  }
}

A Controller utilizing the conversion

@Controller
public class ReportController {
  private List<Report> reports = new ArrayList<>();

  @RequestMapping(value = "/reports",
          method = RequestMethod.POST,
          consumes = "text/report")
  @ResponseBody
  public String handleRequest(@RequestBody Report report) {
      report.setId(reports.size() + 1);
      reports.add(report);
      return "report saved: " + report;
  }

  @RequestMapping(
          value = "/reports/{id}",
          method = RequestMethod.GET)
  @ResponseBody
  public Report reportById(@PathVariable("id") int reportId) {
      if (reportId > reports.size()) {
          throw new RuntimeException("No found for the id :" + reportId);
      }
      return reports.get(reportId - 1);
  }
}

Registering the Converter

@EnableWebMvc
@ComponentScan
public class AppConfig extends WebMvcConfigurerAdapter {

  @Override
  public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
      converters.add(new ReportConverter());
  }
}

Writing a JUnit test

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = AppConfig.class)
public class ReportTests {
  @Autowired
  private WebApplicationContext wac;
  private MockMvc mockMvc;

  @Before
  public void setup() {
      DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
      this.mockMvc = builder.build();
  }

  @Test
  public void testNewReport() throws Exception {
      System.out.println("-- creating new report --");
      MockHttpServletRequestBuilder builder =
              MockMvcRequestBuilders.post("/reports")
                                    .contentType("text/report")
                                    .content(createTestReport());
      mockMvc.perform(builder)
             .andExpect(MockMvcResultMatchers.status()
                                             .isOk())
             .andDo(MockMvcResultHandlers.print());

      System.out.println("\n-- getting report by id --");
      builder = MockMvcRequestBuilders.get("/reports/1");
      mockMvc.perform(builder)
             .andExpect(MockMvcResultMatchers.status()
                                             .isOk())
             .andDo(MockMvcResultHandlers.print());
  }

  private String createTestReport() {
      return "dummy report name\ndummy report content.";
  }
}

Output


-- creating new report --

MockHttpServletRequest:
HTTP Method = POST
Request URI = /reports
Parameters = {}
Headers = {Content-Type=[text/report]}

Handler:
Type = com.logicbig.example.ReportController
Method = public java.lang.String com.logicbig.example.ReportController.handleRequest(com.logicbig.example.Report)

Async:
Async started = false
Async result = null

Resolved Exception:
Type = null

ModelAndView:
View name = null
View = null
Model = null

FlashMap:
Attributes = null

MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[91]}
Content type = text/plain;charset=ISO-8859-1
Body = report saved: Report{id=1, reportName='dummy report name', content='dummy report content.'}
Forwarded URL = null
Redirected URL = null
Cookies = []

-- getting report by id --

MockHttpServletRequest:
HTTP Method = GET
Request URI = /reports/1
Parameters = {}
Headers = {}

Handler:
Type = com.logicbig.example.ReportController
Method = public com.logicbig.example.Report com.logicbig.example.ReportController.reportById(int)

Async:
Async started = false
Async result = null

Resolved Exception:
Type = null

ModelAndView:
View name = null
View = null
Model = null

FlashMap:
Attributes = null

MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[text/report]}
Content type = text/report
Body = dummy report name
dummy report content.
Forwarded URL = null
Redirected URL = null
Cookies = []

Example Project

Dependencies and Technologies Used :

  • spring-webmvc 4.2.4.RELEASE: Spring Web MVC.
  • spring-test 4.2.4.RELEASE: Spring TestContext Framework.
  • javax.servlet-api 3.0.1 Java Servlet API
  • junit 4.12: JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
  • JDK 1.8
  • Maven 3.3.9

Custom HttpMessageConverter Example Select All Download
  • custom-message-converter-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
      • test
        • java
          • com
            • logicbig
              • example

See Also