Pass multiple complex objects to a post/put Web API method

后端 未结 11 1457
南旧
南旧 2020-11-29 21:51

Can some please help me to know how to pass multiple objects from a C# console app to Web API controller as shown below?

using (var httpClient = new System.N         


        
11条回答
  •  半阙折子戏
    2020-11-29 22:06

    I know this is an old question, but I had the same issue and here is what I came up with and hopefully will be useful to someone. This will allow passing JSON formatted parameters individually in request URL (GET), as one single JSON object after ? (GET) or within single JSON body object (POST). My goal was RPC-style functionality.

    Created a custom attribute and parameter binding, inheriting from 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 tcs = new TaskCompletionSource();
            tcs.SetResult(default(AsyncVoid));
            return tcs.Task;
        }
    
        public static HttpParameterBinding HookupParameterBinding(HttpParameterDescriptor descriptor)
        {
            if (descriptor.ActionDescriptor.ControllerDescriptor.GetCustomAttributes().Count == 0 
                && descriptor.ActionDescriptor.GetCustomAttributes().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
        {
        }
    }
    

    Inject binding rule inside WebApiConfig.cs's Register method:

            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 }
            );
            }
    

    This allows for controller actions with default parameter values and mixed complexity, as such:

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

    example post body:

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

    or GET single param (needs URL encoding)

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

    or GET multiple param (needs URL encoding)

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

提交回复
热议问题