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 区分处理结果

相关推荐
神奇小汤圆2 小时前
面试官:如何在 Kafka 中实现延迟消息?
后端
李慕婉学姐2 小时前
【开题答辩过程】以《智慧校园创新互助小程序的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring boot·小程序
请告诉他2 小时前
【实战经验】Dell Inspiron 7560 升级 BIOS 支持 DDR4-2666 内存,解决 Spring Cloud 多模块开发内存瓶颈
后端·spring·spring cloud
我想问问天2 小时前
【从0到1大模型应用开发实战】02|用 LangChain 和本地大模型,完成第一次“可控对话
后端·langchain·aigc
爱吃牛肉的大老虎2 小时前
Spring WebFlux与SpringMVC 对比讲解
java·后端·spring
老华带你飞3 小时前
房屋租赁管理系统|基于java+ vue房屋租赁管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
superman超哥3 小时前
仓颉性能优化秘籍:内联函数的优化策略与深度实践
开发语言·后端·性能优化·内联函数·仓颉编程语言·仓颉·仓颉语言
IT_陈寒4 小时前
Vue3性能优化实战:7个被低估的Composition API技巧让渲染提速40%
前端·人工智能·后端