You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot接口报错时,Xamarin HttpClient的ReasonPhrase始终为空的问题

问题

我的REST API基于Spring Boot开发,客户端采用Xamarin Forms,使用HttpClient与服务端进行数据交互。目前遇到问题:即便服务端返回错误,HttpClient中的ReasonPhrase始终为空。想请教REST API返回错误时需遵循何种特定格式,才能填充该字段?我当前返回的错误为标准JSON结构(示例如下),期望ReasonPhrase能获取到其中的message内容。

示例错误返回格式:

{
    "timestamp": "2022-09-11T17:58:02.811+00:00",
    "status": 404,
    "error": "Not Found",
    "message": "No message available"
}

附上相关代码:

Spring Boot 相关代码

全局异常处理器

@ControllerAdvice
public class AppExceptionsHandler extends ResponseEntityExceptionHandler {

    // 处理自定义异常
    @ExceptionHandler(value = { ApplicationException.class })
    public ResponseEntity<Object> handleUserServiceException(ApplicationException ex, WebRequest request) {
        ErrorMessage errorMessage = new ErrorMessage(new Date(), HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage());

        return new ResponseEntity<>(errorMessage, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    // 处理未知异常
    @ExceptionHandler(value = { Exception.class })
    public ResponseEntity<Object> handleOtherExceptions(Exception ex, WebRequest request) {
        ErrorMessage errorMessage = new ErrorMessage(new Date(), ex.getMessage());

        return new ResponseEntity<>(errorMessage, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

自定义异常类

public class ApplicationException extends RuntimeException{

    private static final long serialVersionUID = 7289264700624277238L;
    
    public ApplicationException(String message)
    {
        super(message);
    }

}

错误消息实体类

public class ErrorMessage {
    private Date timestamp;
    private HttpStatus status;
    private String message;
    
    public ErrorMessage() {}
    
    public ErrorMessage(Date timestamp, HttpStatus status, String message)
    {
        this();
        this.timestamp = timestamp;
        this.message = message;
        this.status = status;
    }
     // 省略getter和setter
}

抛出异常代码

// 业务代码中抛出异常
throw new ApplicationException("Wrong user");

Xamarin HttpClient 请求代码

internal class HttpMethods
{
    private HttpClient _httpClient;

    public HttpMethods()
    {
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(15);
        _httpClient.MaxResponseContentBufferSize = 256000;
        _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
    }

    public async Task<(TA, ErrorDto)> HttpPost<TA, TB>(string url, string token, TB data)
    {
        var result = default(TA);
        var error = new ErrorDto();
        HttpResponseMessage response = new();

        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        HttpContent content;
        content = FileContent(data as string);

        response = await _httpClient.PostAsync(new Uri(url), content);

        if (response.IsSuccessStatusCode)
        {
            var jsonDataResponse = await response.Content.ReadAsStringAsync();
            result = JsonConvert.DeserializeObject<TA>(jsonDataResponse);
        }
        else
        {
            error.ReasonPhrase = (int)response.StatusCode;
            error.StatusCode = (string)response.ReasonPhrase;
        }
                
        return (result, error);
    }
}

解决方案

核心认知纠正

首先要明确:HTTP的ReasonPhrase是响应状态行的一部分,对应标准状态码的默认描述(比如500对应"Internal Server Error"),并非用来承载业务级错误信息。你期望用ReasonPhrase获取响应体JSON中的message是对该字段的误用,REST API的错误信息规范做法是放在响应体中,而非状态行。

另外,你的Xamarin代码存在字段赋值错误:将状态码(int)赋值给ReasonPhrase,将ReasonPhrase(string)赋值给StatusCode,这会导致类型错误,也是你觉得ReasonPhrase为空的原因之一,需先纠正这个错误。


方案一:正确读取响应体中的错误信息(推荐)

这是REST API的标准做法,业务错误信息放在响应体JSON中,客户端解析该JSON获取错误详情。

1. 调整Xamarin代码

修改HttpPost方法的错误分支,读取响应体并解析成对应错误模型:

else
{
    // 读取响应体的错误JSON
    var errorJson = await response.Content.ReadAsStringAsync();
    // 解析为ErrorDto(需确保ErrorDto结构与后端ErrorMessage匹配)
    var errorDetails = JsonConvert.DeserializeObject<ErrorDto>(errorJson);
    
    // 正确赋值:状态码从响应获取,错误消息从解析后的JSON获取
    error.StatusCode = (int)response.StatusCode;
    error.ReasonPhrase = errorDetails.Message;
}

2. 确保ErrorDto结构匹配

定义与后端ErrorMessage对应的客户端模型:

public class ErrorDto
{
    public DateTime Timestamp { get; set; }
    public int Status { get; set; }
    public string Message { get; set; }
    // 以下是你自定义的错误字段,用于返回给上层
    public int StatusCode { get; set; }
    public string ReasonPhrase { get; set; }
}

方案二:强制设置ReasonPhrase(不推荐)

如果一定要让ReasonPhrase承载自定义错误消息,需要在Spring Boot后端显式设置响应状态行的描述。注意:这种做法不符合REST规范,且部分HTTP客户端对ReasonPhrase的长度有限制,仅作为特殊场景的备选方案。

修改Spring Boot全局异常处理器

放弃返回ResponseEntity,直接操作HttpServletResponse设置状态码和ReasonPhrase,同时写入错误JSON响应体:

@ExceptionHandler(value = { ApplicationException.class })
public void handleUserServiceException(ApplicationException ex, WebRequest request, HttpServletResponse response) throws IOException {
    ErrorMessage errorMessage = new ErrorMessage(new Date(), HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage());
    
    // 设置状态码和自定义ReasonPhrase
    response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
    
    // 设置响应类型为JSON并写入错误内容
    response.setContentType("application/json;charset=UTF-8");
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.writeValue(response.getWriter(), errorMessage);
}

此时客户端的response.ReasonPhrase会拿到ex.getMessage()的内容(比如"Wrong user"),同时响应体仍保留完整的错误JSON结构。但请谨慎使用这种方式,避免破坏REST API的规范约定。


内容的提问来源于stack exchange,提问作者JBD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:30:55