如何在Spring Rest Controller中动态设置produces值?
Got it, let's tackle this problem where you need a single Spring Boot endpoint to either return a regular API response or trigger a file download, depending on the apiResponseType parameter. Here are a few practical approaches to dynamically handle the produces value and response behavior:
Approach 1: Use ResponseEntity with Dynamic Content Type
You can declare multiple allowed media types in the produces attribute of @GetMapping, then switch the response details based on your parameter. This keeps your code aligned with Spring's standard response patterns.
import org.springframework.http.*; import org.springframework.core.io.ByteArrayResource; import java.nio.charset.StandardCharsets; @GetMapping(value = "/download", produces = {MediaType.APPLICATION_JSON_VALUE, "text/csv"}) public ResponseEntity<?> downloadFile( @RequestParam(value = "apiResponseType", required = true) String apiResponseType) throws IOException { if ("FILE".equals(apiResponseType)) { String FILE_HEADER = "id,firstName,lastName,gender,age"; byte[] fileContent = FILE_HEADER.getBytes(StandardCharsets.UTF_8); ByteArrayResource resource = new ByteArrayResource(fileContent); // Set headers for file download HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType("text/csv")); headers.setContentDisposition(ContentDisposition.attachment() .filename("user-data.csv") .build()); return ResponseEntity.ok() .headers(headers) .body(resource); } else { // Return your regular API response (e.g., ValidationResponse) ValidationResponse response = new ValidationResponse(); response.setStatus("SUCCESS"); response.setMessage("Returning standard API response"); return ResponseEntity.ok(response); } }
- The
producesattribute lists all valid response types your endpoint can return (JSON and CSV here). - For the file case, we explicitly set the
Content-Dispositionheader to trigger a download, and set the correct media type for the file. - For non-file cases, we return your standard JSON response as usual.
Approach 2: Directly Manipulate HttpServletResponse (More Flexible)
If you want full control without restricting media types upfront, you can bypass ResponseEntity and work directly with HttpServletResponse. This is great for highly dynamic scenarios.
import org.springframework.http.MediaType; import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.charset.StandardCharsets; @GetMapping("/download") public void downloadFile( @RequestParam(value = "apiResponseType", required = true) String apiResponseType, HttpServletResponse response) throws IOException { if ("FILE".equals(apiResponseType)) { String FILE_HEADER = "id,firstName,lastName,gender,age"; // Configure download headers response.setContentType("text/csv"); response.setHeader("Content-Disposition", "attachment; filename=\"user-data.csv\""); // Write file content to response stream try (OutputStream os = response.getOutputStream()) { os.write(FILE_HEADER.getBytes(StandardCharsets.UTF_8)); os.flush(); } } else { // Return JSON response ValidationResponse validationResponse = new ValidationResponse(); validationResponse.setStatus("SUCCESS"); response.setContentType(MediaType.APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getOutputStream(), validationResponse); } }
- No
producesattribute needed here—you set the content type and headers dynamically at runtime. - Just make sure to handle the output stream safely (using try-with-resources to avoid leaks).
Approach 3: Use MediaType.ALL_VALUE for Maximum Flexibility
If you don't want to predefine allowed media types, set produces = MediaType.ALL_VALUE to let Spring accept any request, then explicitly set the content type in your response:
@GetMapping(value = "/download", produces = MediaType.ALL_VALUE) public ResponseEntity<?> downloadFile( @RequestParam(value = "apiResponseType", required = true) String apiResponseType) throws IOException { if ("FILE".equals(apiResponseType)) { String FILE_HEADER = "id,firstName,lastName,gender,age"; ByteArrayResource resource = new ByteArrayResource(FILE_HEADER.getBytes(StandardCharsets.UTF_8)); return ResponseEntity.ok() .contentType(MediaType.parseMediaType("text/csv")) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"user-data.csv\"") .body(resource); } else { ValidationResponse response = new ValidationResponse(); response.setStatus("SUCCESS"); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(response); } }
This is a middle ground between the first two approaches—flexible but still using Spring's ResponseEntity pattern.
Pro Tip
Don't forget to add validation for the apiResponseType parameter (e.g., check that it's either "FILE" or your expected value for API responses) to avoid unexpected behavior.
内容的提问来源于stack exchange,提问作者Krish

