Backend Development 5 min read

Why @ResponseBody Fails When Writing Directly to the Output Stream in Spring

This article analyzes a Spring MVC issue where a batch template download failed due to missing configuration, explores how @ResponseBody interacts with manual stream writes, presents code examples, debugging insights, and offers best‑practice recommendations for reliable file‑download endpoints.

JD Cloud Developers
JD Cloud Developers
JD Cloud Developers
Why @ResponseBody Fails When Writing Directly to the Output Stream in Spring

In Q2 2023 a merchant‑center batch tool could not download its template, returning the JSON error

{"code":-1,"msg":"失败"}

. Investigation revealed two versions of the download code; both worked in pre‑release but failed online because the permission system had not configured the required file, resulting in an empty request.

Exploring @ResponseBody and Direct Stream Writing

The @ResponseBody annotation converts returned objects to JSON. The core processor is

RequestResponseBodyMethodProcessor

, which delegates to

AbstractMessageConverterMethodProcessor#writeWithMessageConverters

.

When a method also writes directly to the response output stream, Spring treats this as an exceptional case and the @ResponseBody effect is bypassed.

Code Example 1 – Simple @ResponseBody

<code>@RequestMapping("/test1")
@ResponseBody
public Map<String, String> test1(HttpServletResponse response) {
    Map<String, String> map = new HashMap<>();
    map.put("1", "1");
    return map;
}
// Returns JSON response</code>

Code Example 2 – Writing a File Stream

<code>@RequestMapping("/test2")
@ResponseBody
public Map<String, String> test2(HttpServletResponse response) throws IOException {
    Map<String, String> map = new HashMap<>();
    map.put("1", "1");
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("Content-Disposition", String.format("attachment; filename=%s_%s.xls", "Demo", System.currentTimeMillis()));
    OutputStream out = response.getOutputStream();
    out.flush();
    out.close();
    return map;
}
// Triggers file download; @ResponseBody has no effect</code>

Debug screenshots (shown below) confirm that Spring treats the stream‑write path as an exception, so the JSON conversion does not occur.

Conclusion and Insights

Trust code that has passed testing; the failure was due to missing environment configuration, not the implementation.

Ensure that permissions, Zookeeper nodes, and other parameters are consistently set in both pre‑release and production environments to avoid omissions.

Suggested Best‑Practice Implementation

<code>@RequestMapping("/test1")
@ResponseBody
public Map<String, String> test1(HttpServletResponse response) {
    Map<String, String> map = new HashMap<>();
    if (fileConfigMissing) {
        map.put("msg", "Missing file configuration");
        return map;
    }
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("Content-Disposition", String.format("attachment; filename=%s_%s.xls", "Demo", System.currentTimeMillis()));
    OutputStream out = response.getOutputStream();
    out.flush();
    out.close();
    return map;
}
</code>

This approach provides clear error messages when configuration is absent while still supporting normal file‑download functionality.

BackendDebuggingJavaspringFileDownloadResponseBody
JD Cloud Developers
Written by

JD Cloud Developers

JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.