简体   繁体   中英

WebAPI JsonConverter for x-www-form-urlencoded not working

I've created simple model with JsonConverter attribute:

public class MyModel
{
    [JsonProperty(PropertyName = "my_to")]
    public string To { get; set; }

    [JsonProperty(PropertyName = "my_from")]
    public string From { get; set; }

    [JsonProperty(PropertyName = "my_date")]
    [JsonConverter(typeof(UnixDateConverter))]
    public DateTime Date { get; set; }
}

and my converter:

public sealed class UnixDateConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (!CanConvert(reader.ValueType))
        {
            throw new JsonSerializationException();
        }

        return DateTimeOffset.FromUnixTimeSeconds((long)reader.Value).ToUniversalTime().LocalDateTime;
    }

    public override bool CanConvert(Type objectType)
    {
        return Type.GetTypeCode(objectType) == TypeCode.Int64;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var datetime = (DateTime) value;
        var dateTimeOffset = new DateTimeOffset(datetime.ToUniversalTime());
        var unixDateTime = dateTimeOffset.ToUnixTimeSeconds();
        writer.WriteValue(unixDateTime);
    }
}

When I send request from Postman and I set content type as application/json everything works fine - my converter works fine, debugger stops at breakpoint in my converter, but I must use x-www-form-urlencoded .

Is there an option to use JsonConverter attribute inside model when sending data as x-www-form-urlencoded ?

I managed to do this by creating custom model binder that implements IModelBinder

Here is generic version of my binder:

internal class GenericModelBinder<T> : IModelBinder where T : class, new()
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof (T))
        {
            return false;
        }

        var model = (T) bindingContext.Model ?? new T();

        JObject @object = null;

        var task = actionContext.Request.Content.ReadAsAsync<JObject>().ContinueWith(t => { @object = t.Result; });
        task.Wait();

        var jsonString = @object.ToString(Formatting.None);
        JsonConvert.PopulateObject(jsonString, model);
        bindingContext.Model = model;

        return true;
    }
}

And here is sample usage:

[Route("save")]
[HttpPost]
public async Task<IHttpActionResult> Save([ModelBinder(typeof (GenericModelBinder<MyModel>))] MyModel model)
{
    try
    {
        //do some stuff with model (validate it, etc)
        await Task.CompletedTask;
        DbContext.SaveResult(model.my_to, model.my_from, model.my_date);
        return Content(HttpStatusCode.OK, "OK", new TextMediaTypeFormatter(), "text/plain");
    }
    catch (Exception e)
    {
        Debug.WriteLine(e);
        Logger.Error(e, "Error saving to DB");
        return InternalServerError();
    }
}

I'm not sure if JsonProperty and JsonConverter attributes worked, but they should.

I'm aware that this might not be the best way to do it, but this code worked for me. Any suggestion are more than welcome.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM