Routing: Datetime parameter passing as null/empty

2020-05-03 02:01发布

问题:

The problem:

Two attributes are needed for a Controller. However, one of them (datetime) goes as null.

Routing

A new routing was incorporated so the Controller could receive two attributes:

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            "RequestHistorial",
            "HechosLiquidadors/Details/{id:int}/{date:datetime}",
            defaults: new { controller = "HechosLiquidadors", action = "Details" });
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });

The datetime parameter doesn't have a format restriction or anything.

How the data is sent

This is the view that sends the parameters:

<table class="table table-bordered table-hover table-striped" id="LiquidacionesList">
<thead>
    <tr>
        {...}
        <th>Resultados</th>
    </tr>
</thead>
<tbody>
    @foreach (var item in Model)
    {
        <tr>
            {...}
            <td>
                <div class="btn-group">
                    <a asp-action="Details" asp-route-id="@item.StoreID" 
                    asp-route-date="@item.FechaLFinLiq" 
                    class="btn btn-default">Consultar</a>
                </div>
           </td>
        </tr>

This table is constructed with a foreach which iterates thru each element of the model and the button at the end receives the attributes needed and using asp-route-id/date these are sent. Note: The table is constructed just fine. All the desired data is shown.

The result

When the button is clicked this is the web address that is shown:

http://localhost:60288/HechosLiquidadors/Details/13?date=21%2F11%2F2017%200%3A00%3A00

For this part: date=21%2F11%2F2017%200%3A00%3A00 I assume the date is going to the controller. However, when I execute this:

public async Task<IActionResult> Details(int? id, DateTime date)
{
    return Content(id + "/" + date);
}

The result is: 11 / 01/01/0001 0:00:00

I believe is a problem with formatting the date that is passing to the Controller? I've been looking for an answer with no luck. Thanks in advance!

UPDATE

It seems like the problems is in how the Get method takes this date. It looks like the Get method expects another format that the one is given and thus it declares the date as null.

Will keep updating.

回答1:

I had the same problem with GET requests and dates in ASP.NET Core 2. If you want to override the default behaviour across the app so it uses the servers default date format for both GET and POST requests see below. Possible room for improvement around parsing the date (ie using TryParseExact etc) and note this doesn't cover nullable datetimes.

This code is heavily based on https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding

public class DateTimeModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        // Specify a default argument name if none is set by ModelBinderAttribute
        var modelName = bindingContext.BinderModelName;
        if (string.IsNullOrEmpty(modelName))
        {
            modelName = bindingContext.ModelName;
        }

        // Try to fetch the value of the argument by name
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var value = valueProviderResult.FirstValue;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(value))
        {
            logger.Debug($"{modelName} was empty or null");
            return Task.CompletedTask;
        }

        if (!DateTime.TryParse(value, out DateTime date))
        {
            bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "Invalid date or format.");
            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(date);
        return Task.CompletedTask;
    }
}

public class DateTimeModelBinderProvider : IModelBinderProvider
{
    private readonly IModelBinder binder = new DateTimeModelBinder();

    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        //Could possibly inspect the context and apply to GETs only if needed?
        return context.Metadata.ModelType == typeof(DateTime) ? binder : null;
    }
}

Then in the StartUp Class > ConfigureServices Method

services.AddMvc(options =>
    {
        //Forces Dates to parse via server format, inserts in the binding pipeline as first but you can adjust it as needed
        options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());
    });


回答2:

This code working for me

asp-route-date="@item.FechaLFinLiq.ToString("u")"

Looks like this is some string to datetime conversion issue