springboot controller返回的是HttpServletResponse成功返回excel文件流,失败就返回失败参数

下面是一个完整的 Spring Boot Controller 示例,根据业务逻辑判断返回 Excel 文件流或失败参数:

方案一:使用 HttpServletResponse 直接输出

复制代码
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/excel")
public class ExcelExportController {

    @PostMapping("/export")
    public void exportExcel(@RequestBody ExportRequest request, 
                           HttpServletResponse response) {
        try {
            // 1. 业务逻辑判断是否成功
            BusinessResult result = processBusiness(request);
            
            if (!result.isSuccess()) {
                // 2. 失败时返回 JSON 格式的错误信息
                response.setContentType("application/json");
                response.setCharacterEncoding("UTF-8");
                
                Map<String, Object> errorResponse = new HashMap<>();
                errorResponse.put("code", 500);
                errorResponse.put("message", result.getErrorMessage());
                errorResponse.put("data", result.getErrorData());
                
                // 设置 HTTP 状态码
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                
                // 写入错误信息
                response.getWriter().write(
                    new com.fasterxml.jackson.databind.ObjectMapper()
                        .writeValueAsString(errorResponse)
                );
                return;
            }
            
            // 3. 成功时返回 Excel 文件
            byte[] excelData = generateExcel(result.getData());
            
            // 设置响应头
            String fileName = URLEncoder.encode("报表.xlsx", StandardCharsets.UTF_8.name());
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            
            // 写入 Excel 文件流
            try (OutputStream os = response.getOutputStream()) {
                os.write(excelData);
                os.flush();
            }
            
        } catch (Exception e) {
            // 异常处理
            handleException(response, e);
        }
    }
    
    // 业务处理方法
    private BusinessResult processBusiness(ExportRequest request) {
        // 模拟业务逻辑
        if (request.getData() == null || request.getData().isEmpty()) {
            return BusinessResult.failure("导出数据不能为空", null);
        }
        
        // 其他业务逻辑...
        return BusinessResult.success("处理成功", request.getData());
    }
    
    // 生成 Excel
    private byte[] generateExcel(List<Data> data) {
        // 使用 Apache POI 或其他库生成 Excel
        // 这里简化为示例
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             XSSFWorkbook workbook = new XSSFWorkbook()) {
            
            XSSFSheet sheet = workbook.createSheet("Sheet1");
            // 填充数据...
            
            workbook.write(baos);
            return baos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("生成Excel失败", e);
        }
    }
    
    // 异常处理方法
    private void handleException(HttpServletResponse response, Exception e) {
        try {
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            
            Map<String, Object> error = new HashMap<>();
            error.put("code", 500);
            error.put("message", "服务器内部错误");
            error.put("detail", e.getMessage());
            
            response.getWriter().write(
                new com.fasterxml.jackson.databind.ObjectMapper()
                    .writeValueAsString(error)
            );
        } catch (Exception ex) {
            // 处理异常时的异常
        }
    }
}

方案二:使用 ResponseEntity 统一返回

复制代码
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.core.io.ByteArrayResource;

@RestController
@RequestMapping("/api/excel")
public class ExcelExportController2 {

    @PostMapping("/export2")
    public ResponseEntity<?> exportExcel(@RequestBody ExportRequest request) {
        try {
            BusinessResult result = processBusiness(request);
            
            if (!result.isSuccess()) {
                // 返回失败的 JSON 响应
                Map<String, Object> errorResponse = new HashMap<>();
                errorResponse.put("code", 400);
                errorResponse.put("message", result.getErrorMessage());
                errorResponse.put("timestamp", System.currentTimeMillis());
                
                return ResponseEntity
                    .status(HttpStatus.BAD_REQUEST)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(errorResponse);
            }
            
            // 生成 Excel
            byte[] excelData = generateExcel(result.getData());
            
            // 设置响应头
            String fileName = "export_" + System.currentTimeMillis() + ".xlsx";
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDisposition(
                ContentDisposition.attachment()
                    .filename(fileName, StandardCharsets.UTF_8)
                    .build()
            );
            headers.setCacheControl("no-cache");
            
            ByteArrayResource resource = new ByteArrayResource(excelData);
            
            return ResponseEntity.ok()
                .headers(headers)
                .contentLength(excelData.length)
                .body(resource);
            
        } catch (Exception e) {
            Map<String, Object> error = new HashMap<>();
            error.put("code", 500);
            error.put("message", "导出失败");
            error.put("detail", e.getMessage());
            
            return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .contentType(MediaType.APPLICATION_JSON)
                .body(error);
        }
    }
}

辅助类定义

复制代码
// 请求参数类
@Data
public class ExportRequest {
    private String startDate;
    private String endDate;
    private List<Data> data;
    
    @Data
    public static class Data {
        private String name;
        private Integer value;
    }
}

// 业务结果类
@Data
@AllArgsConstructor
public class BusinessResult {
    private boolean success;
    private String errorMessage;
    private List<Data> data;
    
    public static BusinessResult success(String message, List<Data> data) {
        return new BusinessResult(true, message, data);
    }
    
    public static BusinessResult failure(String errorMessage, List<Data> data) {
        return new BusinessResult(false, errorMessage, data);
    }
}

使用建议

  1. 方案一​ 更适合需要精细控制 HTTP 响应的场景

  2. 方案二​ 更符合 Spring 的编程风格,代码更简洁

  3. 文件命名 :使用 filename*=UTF-8''编码文件名,支持中文

  4. 响应头设置

    • 成功时设置 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

    • 失败时设置 application/json

  5. 异常处理:确保异常时也能返回规范的错误信息

客户端调用示例

复制代码
// 前端调用
fetch('/api/excel/export', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    startDate: '2024-01-01',
    endDate: '2024-12-31',
    data: [...]
  })
})
.then(async response => {
  const contentType = response.headers.get('content-type');
  
  if (contentType.includes('application/json')) {
    // 错误响应
    const error = await response.json();
    console.error('导出失败:', error.message);
  } else if (contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
    // Excel 文件
    const blob = await response.blob();
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = '报表.xlsx';
    a.click();
  }
});

这样设计可以确保:

  1. 成功时正确下载 Excel 文件

  2. 失败时返回结构化的错误信息

  3. 客户端可以根据 Content-Type 区分处理结果

相关推荐
Charlie_lll9 分钟前
力扣解题-移动零
后端·算法·leetcode
打工的小王1 小时前
Spring Boot(三)Spring Boot整合SpringMVC
java·spring boot·后端
毕设源码-赖学姐1 小时前
【开题答辩全过程】以 高校体育场馆管理系统为例,包含答辩的问题和答案
java·spring boot
vx_Biye_Design1 小时前
【关注可免费领取源码】房屋出租系统的设计与实现--毕设附源码40805
java·spring boot·spring·spring cloud·servlet·eclipse·课程设计
翱翔-蓝天1 小时前
为什么“看起来很规范”的后端项目反而臃肿且性能下降
spring boot
80530单词突击赢2 小时前
JavaWeb进阶:SpringBoot核心与Bean管理
java·spring boot·后端
爬山算法3 小时前
Hibernate(87)如何在安全测试中使用Hibernate?
java·后端·hibernate
WeiXiao_Hyy3 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
苏渡苇3 小时前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
long3163 小时前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法