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

ASP.NET Core中Refit调用DateTime/DateOnly模型绑定失败问题

问题分析与解决方案

核心原因

Postman能正常请求是因为手动传递了纯日期格式的字符串(如01-01-1980),完全匹配API路由对日期参数的预期。而Refit默认序列化DateTime?时,会生成带时间部分的格式(如1980-01-01T00:00:00),其中的冒号:是URL特殊字符,会导致路由匹配失败或模型验证不通过,返回400 Bad Request。

改用DateOnly后出现415错误,是因为API端默认没有配置DateOnly类型的输入格式化器,无法解析Refit传递的参数格式,引发「不支持的媒体类型」错误。

是不是Refit特有问题?

不是。本质是.NET中日期类型的默认序列化格式与API路由/模型绑定的预期不匹配导致的。如果直接使用HttpClient构造请求,若不手动处理日期参数的格式化,同样会遇到相同的400错误——比如直接把DateTime?对象拼到URL里,会自动带上时间部分和冒号,触发同样的问题。

无需手动转换的解决方案

方案1:配置Refit的日期参数格式化

自定义URL参数格式化器,让Refit把DateTime?或DateOnly序列化成API期望的纯日期格式:

// 自定义日期URL格式化器
public class DateRouteFormatter : IUrlParameterFormatter
{
    public string Format(object value)
    {
        // 处理DateTime?类型
        if (value is DateTime? dateTime)
        {
            return dateTime?.ToString("MM-dd-yyyy") ?? string.Empty;
        }
        // 处理DateOnly类型
        if (value is DateOnly dateOnly)
        {
            return dateOnly.ToString("MM-dd-yyyy");
        }
        // 其他类型默认处理
        return value?.ToString() ?? string.Empty;
    }
}

注册Refit客户端时指定该格式化器:

services.AddRefitClient<ICustomerApi>()
    .ConfigureHttpClient(c => c.BaseAddress = new Uri("你的API基础地址"))
    .AddUrlParameterFormatter(new DateRouteFormatter());

方案2:API端调整路由约束与模型绑定

在API项目中配置路由对日期格式的支持,让它能识别Refit默认的日期格式:

  1. 注册自定义日期路由约束:
// Program.cs中
builder.Services.Configure<RouteOptions>(options =>
{
    // 添加自定义日期约束,支持多种格式
    options.ConstraintMap.Add("date", typeof(CustomDateRouteConstraint));
});

// 自定义日期约束实现
public class CustomDateRouteConstraint : IRouteConstraint
{
    public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (!values.TryGetValue(routeKey, out var value) || value == null)
            return false;
        
        // 支持多种日期格式解析
        return DateTime.TryParseExact(value.ToString(), 
            new[] { "MM-dd-yyyy", "yyyy-MM-dd", "yyyy-MM-ddTHH:mm:ss" }, 
            CultureInfo.InvariantCulture, 
            DateTimeStyles.None, 
            out _);
    }
}
  1. 修改控制器路由,添加约束:
[HttpGet("Customers/{DateOfBirth:date}")]
public Task<IActionResult> GetCustomers([FromRoute] DateTime? dateOfBirth, CancellationToken token)
{
    // 业务逻辑
}

方案3:API端添加DateOnly输入格式化器(针对DateOnly场景)

如果想用DateOnly类型,在API项目中添加DateOnly的输入格式化器,让框架能正确解析参数:

// Program.cs中
builder.Services.AddControllers(options =>
{
    options.InputFormatters.Insert(0, new DateOnlyInputFormatter());
});

// 自定义DateOnly输入格式化器
public class DateOnlyInputFormatter : InputFormatter
{
    public DateOnlyInputFormatter()
    {
        SupportedMediaTypes.Add(MediaTypeNames.Text.Plain);
        SupportedMediaTypes.Add(MediaTypeNames.Application.Json);
    }

    protected override bool CanReadType(Type type)
    {
        return type == typeof(DateOnly) || type == typeof(DateOnly?);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var request = context.HttpContext.Request;
        using var reader = new StreamReader(request.Body);
        var content = await reader.ReadToEndAsync();
        
        if (DateOnly.TryParse(content, out var date))
        {
            return await InputFormatterResult.SuccessAsync(date);
        }
        
        return await InputFormatterResult.FailureAsync();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 15:48:30