2015-12-21 70 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