WebAPI发布JSON字符串并将其映射到模型

时间:2016-07-18 14:23:38

标签: c# asp.net-web-api asp.net-web-api2

我必须创建将使用JSON消息的webhook端点 消息以x-www-form-urlencoded格式发送:

  

key = json
  value = {“user_Id”:“728409840”,“call_id”:“1114330”,“answers_time”:“2015-04-16 15:37:47”}

如PostMan所示:

enter image description here

请求如下:

  

JSON =%7B%22user_Id%22%3A +%22728409840%22%2C +%22call_id%22%3A +%221114330%22%2C%22answered_time%22%3A +%222015-04-16 + 15%3A37%3A47% 22%7D

要从请求中获取值作为我的类(模型),我必须创建包含单个字符串属性的临时对象:

public class Tmp
{
    public string json { get; set; }
}

和我的控制器中使用该请求的方法:

[AllowAnonymous]
[Route("save_data")]
[HttpPost]
public IHttpActionResult SaveData(Tmp tmp)
{
    JObject json2 = JObject.Parse(tmp.json);
    var details = json2.ToObject<CallDetails>();
    Debug.WriteLine(details);
    //data processing
    return Content(HttpStatusCode.OK, "OK", new TextMediaTypeFormatter(), "text/plain");
}

正如你所看到的,Tmp课程毫无用处。

有没有办法将请求数据作为此类获取:

public class CallDetails
{
    public string UserId { get; set; }
    public string CallId { get; set; }
    public string AnsweredTime { get; set; }
}

我知道IModelBinder课程,但在开始之前,我想知道是否有更简单的方法。

我不能改变网页请求格式,格式我的意思是它总是包含单个键的POST - JSON你有json字符串作为值。

3 个答案:

答案 0 :(得分:2)

您可以使用JsonProperty属性将json对象属性映射到c#对象属性:

public class CallDetails
{
    [JsonProperty("user_id")]
    public string UserId { get; set; }
    [JsonProperty("call_id")]
    public string CallId { get; set; }
    [JsonProperty("answered_time")]
    public string AnsweredTime { get; set; }
}

然后它可以在没有临时类的情况下使用:

[AllowAnonymous]
[Route("save_data")]
[HttpPost]
public IHttpActionResult SaveData(CallDetails callDetails)

更新。因为数据是以x-www-form-urlencoded的形式发送的 - 我认为你处理它的方式最简单,也不是那么糟糕。如果您想在此处查看其他选项,请参阅其中一些选项:

选项1 - 自定义模型绑定器。像这样:

public class CustomModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var body = actionContext.Request.Content.ReadAsStringAsync().Result;
        body = body.Replace("json=", "");
        var json = HttpUtility.UrlDecode(body);

        bindingContext.Model = JsonConvert.DeserializeObject<CallDetails>(json);

        return true;
    }
}

用法:SaveData([ModelBinder(typeof(CustomModelBinder))]CallDetails callDetails)。缺点 - 您将失去验证,也可能会失去web api管道中定义的其他内容。

选项2 - DelegatingHandler

public class NormalizeHandler : DelegatingHandler
{
    public NormalizeHandler(HttpConfiguration httpConfiguration)
    {
        InnerHandler = new HttpControllerDispatcher(httpConfiguration);
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var source = await request.Content.ReadAsStringAsync();
        source = source.Replace("json=", "");
        source = HttpUtility.UrlDecode(source);

        request.Content = new StringContent(source, Encoding.UTF8, "application/json");

        return await base.SendAsync(request, cancellationToken);
    }
}

用法:

[AllowAnonymous]
[HttpPost]
public IHttpActionResult SaveData(CallDetails callDetails)

缺点 - 你需要为它定义自定义路线:

config.Routes.MapHttpRoute(
            name: "save_data",
            routeTemplate: "save_data",
            defaults: new { controller = "YourController", action = "SaveData" },
            constraints: null,
            handler: new NormalizeHandler(config)
        );

答案 1 :(得分:0)

NewtonSoft的Json.NET可以帮助你deserialize an object。如果您的json属性名称与您的实际类名不匹配,您可以编写自定义转换器来帮助。

修改

如果您使用的是MVC6,可以试试这个。将您的参数从Tmp类型更改为CallDetails类型,并使用属性[FromBody]进行标记,如下所示:

public IHttpActionResult SaveData([FromBody]CallDetails details)

例如,请参阅&#34;不同的模型绑定&#34;在this post。但是,我仍然认为您需要手动反序列化,因为类CallDetails的属性名称与传入的JSON属性完全匹配。

答案 2 :(得分:0)

你不要忘记在使用JObject.Parse之前解码编码的url吗?它可能有效。并且对象的属性与json atributes

不匹配