c# - 設計書 rest ポスト/プットWeb APIメソッドに複数の複雑なオブジェクトを渡す



5 Answers

@djikayが述べたように、複数のFromBodyパラメータを渡すことはできません。

私が持っている回避策の1つは、 CompositeObjectを定義することです。

public class CompositeObject
{
    public Content Content { get; set; }
    public Config Config { get; set; }
}

代わりにあなたのWebAPIがこのCompositeObjectをパラメータとして受け取るようにしてください。

public void StartProcessiong([FromBody] CompositeObject composite)
{ ... }
rest api 命名規則

C#コンソールアプリケーションからWeb APIコントローラに複数のオブジェクトを渡す方法を知ってもらえますか?

using (var httpClient = new System.Net.Http.HttpClient())
{
    httpClient.BaseAddress = new Uri(ConfigurationManager.AppSettings["Url"]);
    httpClient.DefaultRequestHeaders.Accept.Clear();
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));   

    var response = httpClient.PutAsync("api/process/StartProcessiong", objectA, objectB);
}

私のWeb APIメソッドは次のようなものです:

public void StartProcessiong([FromBody]Content content, [FromBody]Config config)
{

}



私はこれが古い質問であることを知っていますが、私は同じ問題を抱えていました。ここで私が思いついたことがあり、うまくいけば誰かに役立つでしょう。 これにより、リクエストURL(GET)内に個別にJSON形式のパラメータを1つのJSONオブジェクトとして渡すことができます。 (GET)または単一のJSON本体オブジェクト(POST)内で実行されます。 私の目標はRPCスタイルの機能でした。

カスタム属性とパラメータバインディングを作成し、HttpParameterBindingから継承します。

public class JSONParamBindingAttribute : Attribute
{

}

public class JSONParamBinding : HttpParameterBinding
{

    private static JsonSerializer _serializer = JsonSerializer.Create(new JsonSerializerSettings()
    {
        DateTimeZoneHandling = DateTimeZoneHandling.Utc
    });


    public JSONParamBinding(HttpParameterDescriptor descriptor)
        : base(descriptor)
    {
    }

    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                                HttpActionContext actionContext,
                                                CancellationToken cancellationToken)
    {
        JObject jobj = GetJSONParameters(actionContext.Request);

        object value = null;

        JToken jTokenVal = null;
        if (!jobj.TryGetValue(Descriptor.ParameterName, out jTokenVal))
        {
            if (Descriptor.IsOptional)
                value = Descriptor.DefaultValue;
            else
                throw new MissingFieldException("Missing parameter : " + Descriptor.ParameterName);
        }
        else
        {
            try
            {
                value = jTokenVal.ToObject(Descriptor.ParameterType, _serializer);
            }
            catch (Newtonsoft.Json.JsonException e)
            {
                throw new HttpParseException(String.Join("", "Unable to parse parameter: ", Descriptor.ParameterName, ". Type: ", Descriptor.ParameterType.ToString()));
            }
        }

        // Set the binding result here
        SetValue(actionContext, value);

        // now, we can return a completed task with no result
        TaskCompletionSource<AsyncVoid> tcs = new TaskCompletionSource<AsyncVoid>();
        tcs.SetResult(default(AsyncVoid));
        return tcs.Task;
    }

    public static HttpParameterBinding HookupParameterBinding(HttpParameterDescriptor descriptor)
    {
        if (descriptor.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<JSONParamBindingAttribute>().Count == 0 
            && descriptor.ActionDescriptor.GetCustomAttributes<JSONParamBindingAttribute>().Count == 0)
            return null;

        var supportedMethods = descriptor.ActionDescriptor.SupportedHttpMethods;

        if (supportedMethods.Contains(HttpMethod.Post) || supportedMethods.Contains(HttpMethod.Get))
        {
            return new JSONParamBinding(descriptor);
        }

        return null;
    }

    private JObject GetJSONParameters(HttpRequestMessage request)
    {
        JObject jobj = null;
        object result = null;
        if (!request.Properties.TryGetValue("ParamsJSObject", out result))
        {
            if (request.Method == HttpMethod.Post)
            {
                jobj = JObject.Parse(request.Content.ReadAsStringAsync().Result);
            }
            else if (request.RequestUri.Query.StartsWith("?%7B"))
            {
                jobj = JObject.Parse(HttpUtility.UrlDecode(request.RequestUri.Query).TrimStart('?'));
            }
            else
            {
                jobj = new JObject();
                foreach (var kvp in request.GetQueryNameValuePairs())
                {
                    jobj.Add(kvp.Key, JToken.Parse(kvp.Value));
                }
            }
            request.Properties.Add("ParamsJSObject", jobj);
        }
        else
        {
            jobj = (JObject)result;
        }

        return jobj;
    }



    private struct AsyncVoid
    {
    }
}

WebApiConfig.csのRegisterメソッド内にバインディングルールを挿入する:

        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.ParameterBindingRules.Insert(0, JSONParamBinding.HookupParameterBinding);

            config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        }

これにより、以下のように、デフォルトのパラメータ値と複雑な複雑さのコントローラアクションが可能になります。

[JSONParamBinding]
    [HttpPost, HttpGet]
    public Widget DoWidgetStuff(Widget widget, int stockCount, string comment="no comment")
    {
        ... do stuff, return Widget object
    }

ポストボディの例:

{ 
    "widget": { 
        "a": 1, 
        "b": "string", 
        "c": { "other": "things" } 
    }, 
    "stockCount": 42, 
    "comment": "sample code"
} 

またはGET単一パラメータ(URLエンコーディングが必要)

controllerPath/DoWidgetStuff?{"widget":{..},"comment":"test","stockCount":42}

またはGET複数パラメータ(URLエンコーディングが必要)

controllerPath/DoWidgetStuff?widget={..}&comment="test"&stockCount=42



あなたに役立つかもしれない別のパターンがあります。 それはゲットだけど、ポスト/プットにも同じ原則とコードが適用されますが、逆です。 基本的には、オブジェクトをこのObjectWrapperクラスに変換するという原則に基づいて動作します。このクラスは、Typeの名前を他の側に保持します。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace WebAPI
{
    public class ObjectWrapper
    {
        #region Public Properties
        public string RecordJson { get; set; }
        public string TypeFullName { get; set; }
        #endregion

        #region Constructors

        public ObjectWrapper() : this(null, null)
        {
        }

        public ObjectWrapper(object objectForWrapping) : this(objectForWrapping, null)
        {
        }

        public ObjectWrapper(object objectForWrapping, string typeFullName)
        {
            if (typeFullName == null && objectForWrapping != null)
            {
                TypeFullName = objectForWrapping.GetType().FullName;
            }
            else
            {
                TypeFullName = typeFullName;
            }

            RecordJson = JsonConvert.SerializeObject(objectForWrapping);
        }
        #endregion

        #region Public Methods
        public object ToObject()
        {
            var type = Type.GetType(TypeFullName);
            return JsonConvert.DeserializeObject(RecordJson, type);
        }
        #endregion

        #region Public Static Methods
        public static List<ObjectWrapper> WrapObjects(List<object> records)
        {
            var retVal = new List<ObjectWrapper>();
            records.ForEach
            (item =>
            {
                retVal.Add
                (
                    new ObjectWrapper(item)
                );
            }
            );

            return retVal;
        }

        public static List<object> UnwrapObjects(IEnumerable<ObjectWrapper> objectWrappers)
        {
            var retVal = new List<object>();

            foreach(var item in objectWrappers)
            {
                retVal.Add
                (
                    item.ToObject()
                );
            }

            return retVal;
        }
        #endregion
    }
}

RESTコード:

[HttpGet]
public IEnumerable<ObjectWrapper> Get()
{
    var records = new List<object>();
    records.Add(new TestRecord1());
    records.Add(new TestRecord2());
    var wrappedObjects = ObjectWrapper.WrapObjects(records);
    return wrappedObjects;
}

これは、RESTクライアント・ライブラリーを使用するクライアント側(UWP)のコードです。 クライアントライブラリは、Newtonsoft Jsonのシリアライズライブラリを使用しています。

private static async Task<List<object>> Getobjects()
{
    var result = await REST.Get<List<ObjectWrapper>>("http://localhost:50623/api/values");
    var wrappedObjects = (IEnumerable<ObjectWrapper>) result.Data;
    var unwrappedObjects =  ObjectWrapper.UnwrapObjects(wrappedObjects);
    return unwrappedObjects;
}



ここでは、 JObjectを使用してjqueryからWEB APIに複数の汎用オブジェクト(jsonとして)をJObjectてから、必要な特定のオブジェクト型をAPIコントローラーにキャストする回避策を見つけました。 このオブジェクトは、JSONを操作するために特別に設計された具体的な型を提供します。

var combinedObj = {}; 
combinedObj["obj1"] = [your json object 1]; 
combinedObj["obj2"] = [your json object 2];

$http({
       method: 'POST',
       url: 'api/PostGenericObjects/',
       data: JSON.stringify(combinedObj)
    }).then(function successCallback(response) {
         // this callback will be called asynchronously
         // when the response is available
         alert("Saved Successfully !!!");
    }, function errorCallback(response) {
         // called asynchronously if an error occurs
         // or server returns response with an error status.
         alert("Error : " + response.data.ExceptionMessage);
});

このオブジェクトをあなたのコントローラに入れることができます

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public [OBJECT] PostGenericObjects(object obj)
    {
        string[] str = GeneralMethods.UnWrapObjects(obj);
        var item1 = JsonConvert.DeserializeObject<ObjectType1>(str[0]);
        var item2 = JsonConvert.DeserializeObject<ObjectType2>(str[1]);

        return *something*;
    } 

複雑なオブジェクトをアンラップする汎用関数を作ったので、送信およびアンラッピング中にオブジェクトの数に制限はありません。 私たちは2つ以上のオブジェクトを送ることさえできます

public class GeneralMethods
{
    public static string[] UnWrapObjects(object obj)
    {
        JObject o = JObject.Parse(obj.ToString());

        string[] str = new string[o.Count];

        for (int i = 0; i < o.Count; i++)
        {
            string var = "obj" + (i + 1).ToString();
            str[i] = o[var].ToString(); 
        }

        return str;
    }

}

私は簡単に統合するための簡単なコードで、もう少し詳しく説明して、私のブログにソリューションを投稿しました。

複数の複雑なオブジェクトをWeb APIに渡す

私はそれが誰かを助けることを望む。 私は、この方法論を使うことの長所と短所について、ここで専門家から聞くことに興味があります。




webapiサービスに複数の複雑なオブジェクトを渡す最善の方法は、動的クラス、json文字列、カスタムクラス以外のタプルを使用することです。

HttpClient.PostAsJsonAsync("http://Server/WebService/Controller/ServiceMethod?number=" + number + "&name" + name, Tuple.Create(args1, args2, args3, args4));

[HttpPost]
[Route("ServiceMethod")]
[ResponseType(typeof(void))]
public IHttpActionResult ServiceMethod(int number, string name, Tuple<Class1, Class2, Class3, Class4> args)
{
    Class1 c1 = (Class1)args.Item1;
    Class2 c2 = (Class2)args.Item2;
    Class3 c3 = (Class3)args.Item3;
    Class4 c4 = (Class4)args.Item4;
    /* do your actions */
    return Ok();
}

タプルを使用している間に渡されたオブジェクトを直列化および逆直列化する必要はありません。 あなたが最後のタプル引数のための7つ以上の複合オブジェクト作成内部タプルオブジェクトを送信したい場合。




Related