diff --git a/Refit-Tests/RequestBuilder.cs b/Refit-Tests/RequestBuilder.cs index af4d06730..b17906863 100644 --- a/Refit-Tests/RequestBuilder.cs +++ b/Refit-Tests/RequestBuilder.cs @@ -33,6 +33,9 @@ public interface IRestMethodInfoTests [Get("/foo/bar/{id}")] IObservable FetchSomeStuffWithBody([AliasAs("id")] int anId, [Body] Dictionary theData); + [Post("/foo/bar/{id}")] + IObservable PostSomeUrlEncodedStuff([AliasAs("id")] int anId, [Body(BodySerializationMethod.UrlEncoded)] Dictionary theData); + [Get("/foo/bar/{id}")] [Headers("Api-Version: 2 ")] Task FetchSomeStuffWithHardcodedHeaders(int id); @@ -132,6 +135,18 @@ public void FindTheBodyParameter() Assert.AreEqual(1, fixture.BodyParameterInfo.Item2); } + [Test] + public void AllowUrlEncodedContent() + { + var input = typeof(IRestMethodInfoTests); + var fixture = new RestMethodInfo(input, input.GetMethods().First(x => x.Name == "PostSomeUrlEncodedStuff")); + Assert.AreEqual("id", fixture.ParameterMap[0]); + + Assert.IsNotNull(fixture.BodyParameterInfo); + Assert.AreEqual(0, fixture.QueryParameterMap.Count); + Assert.AreEqual(BodySerializationMethod.UrlEncoded, fixture.BodyParameterInfo.Item1); + } + [Test] public void HardcodedHeadersShouldWork() { @@ -227,6 +242,9 @@ public interface IDummyHttpApi [Get("/void")] Task FetchSomeStuffWithVoid(); + [Post("/foo/bar/{id}")] + Task PostSomeUrlEncodedStuff(int id, [Body(BodySerializationMethod.UrlEncoded)] object content); + string SomeOtherMethod(); } @@ -423,5 +441,17 @@ public void HttpClientShouldNotPrefixEmptyAbsolutePathToTheRequestUri() Assert.AreEqual("http://api/foo/bar/42", testHttpMessageHandler.RequestMessage.RequestUri.ToString()); } + + [Test] + public void BodyContentGetsUrlEncoded() + { + var fixture = new RequestBuilderImplementation(typeof(IDummyHttpApi)); + var factory = fixture.BuildRequestFactoryForMethod("PostSomeUrlEncodedStuff"); + var output = factory(new object[] {6, new {Foo = "Something", Bar = 100, Baz = (string) null}}); + + string content = output.Content.ReadAsStringAsync().Result; + + Assert.AreEqual("Foo=Something&Bar=100&Baz=", content); + } } } \ No newline at end of file diff --git a/Refit/Attributes.cs b/Refit/Attributes.cs index 9dbdf52a6..34c977495 100644 --- a/Refit/Attributes.cs +++ b/Refit/Attributes.cs @@ -70,7 +70,7 @@ public override HttpMethod Method { } public enum BodySerializationMethod { - Json, Xml, + Json, UrlEncoded } [AttributeUsage(AttributeTargets.Parameter)] diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index ebc294473..91e076158 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Net.Http; using System.Collections.Generic; using System.Linq; @@ -82,7 +83,15 @@ public Func BuildRequestFactoryForMethod(string me } else if (stringParam != null) { ret.Content = new StringContent(stringParam); } else { - ret.Content = new StringContent(JsonConvert.SerializeObject(paramList[i]), Encoding.UTF8, "application/json"); + switch (restMethod.BodyParameterInfo.Item1) { + case BodySerializationMethod.UrlEncoded: + ret.Content = new FormUrlEncodedContent(new FormValueDictionary(paramList[i])); + break; + case BodySerializationMethod.Json: + ret.Content = new StringContent(JsonConvert.SerializeObject(paramList[i]), Encoding.UTF8, "application/json"); + break; + } + } continue; @@ -511,4 +520,37 @@ void determineReturnTypeInfo(MethodInfo methodInfo) throw new ArgumentException("All REST Methods must return either Task or IObservable"); } } + + class FormValueDictionary : Dictionary + { + // Can't use ConcurrentDictionary because Silverlight doesn't have it + private static readonly Dictionary propertyCache + = new Dictionary(); + + public FormValueDictionary(object source) { + if (source == null) return; + + var dictionary = source as IDictionary; + if (dictionary != null) { + foreach (var key in dictionary.Keys) { + Add(key.ToString(), string.Format("{0}", dictionary[key])); + } + } + else { + var type = source.GetType(); + if (!propertyCache.ContainsKey(type)) { + propertyCache[type] = getProperties(type); + } + foreach (var property in propertyCache[type]) { + Add(property.Name, string.Format("{0}", property.GetValue(source, null))); + } + } + } + + PropertyInfo[] getProperties(Type type) { + return type.GetProperties() + .Where(p => p.CanRead) + .ToArray(); + } + } }