2015-12-21 32 views
0

我得到了一个WebApi OData服务,我需要一个采取任意json参数的操作。webapi odata中的大json参数

有没有办法让JObject这样的东西成为OData动作参数? action.Parameter<JObject>()不起作用。

复杂类型'Newtonsoft.Json.Linq.JContainer'通过属性'Parent'引用自身。不允许使用复杂类型的递归循环。

它使用字符串参数,但它意味着在所有请求的两端不必要的转换。 json也可能很大(100kb +),所以我认为它会对大对象堆施加压力以使用字符串。

回答

1

OData服务通常是强类型的,因此您必须竭尽全力克服Web API内置的从JSON到CLR类型的映射。

首先,定义一个读取JTokens的新媒体类型格式程序。请注意正在使用的自定义媒体类型。

public class RawJsonMediaTypeFormatter : MediaTypeFormatter 
{ 
    private static readonly MediaTypeHeaderValue _customMediaType = 
     MediaTypeHeaderValue.Parse("application/prs.adrianm+json"); 

    public RawJsonMediaTypeFormatter() : base() 
    { 
     this.Intialize(); 
    } 

    protected RawJsonMediaTypeFormatter(MediaTypeFormatter formatter) : base(formatter) 
    { 
     this.Intialize(); 
    } 

    protected void Intialize() 
    { 
     this.SupportedMediaTypes.Add(_customMediaType); 
    } 

    public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType) 
    { 
     if (type == typeof(JToken) && mediaType.MediaType == _customMediaType.MediaType) 
     { 
      return this; 
     } 

     return base.GetPerRequestFormatterInstance(type, request, mediaType); 
    } 

    public override bool CanReadType(Type type) 
    { 
     return type == typeof(JToken); 
    } 

    public override bool CanWriteType(Type type) 
    { 
     return false; 
    } 

    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) 
    { 
     return this.ReadFromStreamAsync(type, readStream, content, formatterLogger, default(CancellationToken)); 
    } 

    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken) 
    { 
     object result; 

     using (var reader = new JsonTextReader(new StreamReader(readStream))) 
     { 
      result = JToken.ReadFrom(reader); 
     } 

     return Task.FromResult(result); 
    } 
} 

随着Web API注册的一部分,自定义格式的一个实例添加到HttpConfiguration并宣布你的OData行动。请注意,动作参数的类型是vanilla object

 config.Formatters.Clear(); 
     config.Formatters.AddRange(ODataMediaTypeFormatters.Create()); 
     config.Formatters.Add(new RawJsonMediaTypeFormatter()); 

     var builder = new ODataConventionModelBuilder(); 
     var arbitraryJsonAction = builder.Action("ArbitraryJson"); 
     arbitraryJsonAction.Parameter<object>("json"); 
     arbitraryJsonAction.Returns<string>(); 

为操作添加控制器方法。

[HttpPost] 
    [ODataRoute("ArbitraryJson")] 
    public IHttpActionResult ArbitraryJson(JToken json) 
    { 
     return this.Ok(json.ToString()); 
    } 

在客户端,记得设置Content-Type通过自定义格式处理的自定义介质类型:

POST http://host/ArbitraryJson 
Content-Type: application/prs.adrianm+json 

[1, 2, {"foo": true }] 

响应有效载荷看起来应该像下面这样:

{ 
    "@odata.context": "http://host/$metadata#Edm.String", 
    "value": "[\r\n 1,\r\n 2,\r\n {\r\n \"foo\": true\r\n }\r\n]" 
} 
+0

非常很好的答案。希望我能为你的努力做更多努力。对于不同的内容类型的需求可能是一种破坏我的情况,但我明确地从你的帖子中学到了很多东西。谢谢。 – adrianm

+0

你可以尝试使用带有额外参数的'application/json'。例如,'application/json; format = raw'。 – lencharest