Angular中如何获取响应内容及自定义响应头?
Angular中获取Spring Boot异常响应内容及自定义响应头的问题
问题场景
Angular项目嵌入JSP页面,向Spring Boot后端发送带多部分文件的POST请求。当文件大小超过10MB时,Spring Boot抛出MaxUploadSizeExceededException,已通过全局异常处理器封装为EsignError格式返回,并添加自定义响应头msgHeader。浏览器Network标签能看到完整响应内容和自定义头,但Angular组件/Interceptor中无法正确获取。
后端代码实现
全局异常处理器EsignExceptionHandler
@ControllerAdvice @Slf4j public class EsignExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(value = {EsignException.class}) public ResponseEntity<Object> scannerException(Exception ex, WebRequest request) { log.error(ex.getMessage()); log.info("Inside first handler"); String bodyOfResponse = ex.getMessage(); return handleExceptionInternal(ex, bodyOfResponse, HTTPUtil.getCommonHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); } @ExceptionHandler(MaxUploadSizeExceededException.class) public @ResponseBody ResponseEntity<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException ex) { log.error(ex.getMessage()); log.info("Inside second handler"); EsignError error = new EsignError(HttpStatus.BAD_REQUEST.value(), ex.getMessage()); HttpHeaders headers = new HttpHeaders(HTTPUtil.getCommonHeaders()); headers.setAccessControlAllowOrigin("*"); headers.setContentType(MediaType.APPLICATION_JSON); headers.add("msgHeader", ex.getMessage()); return new ResponseEntity<>(error, headers, HttpStatus.BAD_REQUEST); } }
EsignError类定义
@Data public class EsignError { private int status; private String message; private Date timestamp; public EsignError(int status, String message) { this.status = status; this.message = message; this.timestamp = new Date(); } }
后端返回示例响应
{ "status": 400, "message": "Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (31238283) exceeds the configured maximum (10485760)", "timestamp": "2022-11-17T08:41:45.580+00:00" }
Angular组件代码(postFile方法)
postFile() { this.uploadState = 'отправка файла...'; var certHolders: string = ''; this.corresp.approvals.forEach(a => { certHolders = certHolders + a.logname + ';'; }); const formData: FormData = new FormData(); formData.append('file', this.file, 'file.pdf'); formData.append('idappli', String(this.corresp.idappli)); formData.append('odcorresp', String(this.corresp.odcorresp)); formData.append('idletter', String(this.corresp.idletter)); formData.append('certHolders', certHolders); formData.append('fpage', String(this.stampStart)); formData.append('lpage', String(this.stampEnd)); const headers = new HttpHeaders().append("Access-Control-Allow-Origin", "*") .append("Access-Control-Allow-Origin", "*") .append("Access-Control-Allow-Methods", "POST") .append("Access-Control-Allow-Headers", "Origin, Accept, Authorization, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers") const o: Observable<any> = this.http.post(this.corresp.url, formData, {headers, responseType: "blob"}); o.subscribe(blob => { this.addHistory("insert"); this.getFileWithSign(); this.corresp.exists = true; console.log('blob.error: ' + blob.error); console.log('blob.message: ' + blob.message); }, error => { this.uploadState = 'ошибка отправки файла на подпись!'; console.log('console.log error.status: ' + error.status); // console.log('console.log error.error(): ' + error.error()); // console.log('console.log error.errorDetails: ' + error.errorDetails); console.log('console.log error: ' + error); console.log('console.log error.error: ' + error.error); // The following line results in error in browser console: ERROR TypeError: Cannot read properties of undefined (reading 'status') // console.log('console.log error.error.status: ' + error.error.status); if (error.status === 400) { const msg2 = error.headers.get('msgHeader'); alert('msg2: ' + msg2); } } ); }
解决方案
1. 后端配置自定义头暴露
跨域场景下,自定义响应头必须通过Access-Control-Expose-Headers主动暴露给前端,否则Angular无法读取。修改后端异常处理器的header配置:
// 在handleMaxUploadSizeExceededException方法中添加 headers.add("Access-Control-Expose-Headers", "msgHeader");
2. Angular端调整错误响应处理
当前请求设置了responseType: "blob",错误响应的error是Blob对象,需先转换为JSON才能解析EsignError内容:
error => { this.uploadState = 'ошибка отправки файла на подпись!'; console.log('console.log error.status: ' + error.status); // 解析Blob类型的错误响应 if (error.error instanceof Blob) { const reader = new FileReader(); reader.onload = () => { const errorData = JSON.parse(reader.result as string); console.log('错误状态:', errorData.status); console.log('错误消息:', errorData.message); }; reader.readAsText(error.error); } // 获取自定义响应头 if (error.status === 400) { const msg2 = error.headers.get('msgHeader'); alert('msg2: ' + msg2); } }
3. 移除Angular请求中冗余的CORS头
Access-Control-Allow-Origin等属于后端返回的响应头,前端请求时无需手动携带,简化请求配置:
// 移除手动添加的CORS相关头,Angular会自动处理跨域请求 const o: Observable<any> = this.http.post(this.corresp.url, formData, {responseType: "blob"});
4. Interceptor中获取响应头(可选)
若使用Interceptor统一处理错误,需确保在HttpErrorResponse中读取头,同时后端已完成自定义头暴露:
@Injectable() export class ErrorInterceptor implements HttpInterceptor { constructor() {} intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request).pipe( catchError((error: HttpErrorResponse) => { // 获取自定义响应头 const msgHeader = error.headers.get('msgHeader'); console.log('自定义头msgHeader:', msgHeader); // 解析Blob类型错误 if (error.error instanceof Blob) { return from(new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const errorData = JSON.parse(reader.result as string); reject(new HttpErrorResponse({ ...error, error: errorData })); }; reader.readAsText(error.error); })); } return throwError(() => error); }) ); } }
内容的提问来源于stack exchange,提问作者Nikolay
相关产品推荐
相关产品推荐

