43 changes: 43 additions & 0 deletions RestSharp.Tests/SampleClasses.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RestSharp.Tests
{
public class Person
{
public string Name { get; set; }
public DateTime StartDate { get; set; }
public int Age { get; set; }
public decimal Percent { get; set; }
public long BigNumber { get; set; }
public bool IsCool { get; set; }
public List<Friend> Friends { get; set; }
public Friend BestFriend { get; set; }

protected string Ignore { get; set; }
public string IgnoreProxy { get { return Ignore; } }

protected string ReadOnly { get { return null; } }
public string ReadOnlyProxy { get { return ReadOnly; } }

public FoeList Foes { get; set; }
}

public class Friend
{
public string Name { get; set; }
public int Since { get; set; }
}

public class Foe
{
public string Nickname { get; set; }
}

public class FoeList : List<Foe>
{
public string Team { get; set; }
}
}
188 changes: 188 additions & 0 deletions RestSharp.Tests/XmlTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using System.Xml.Linq;
using RestSharp.Deserializers;

namespace RestSharp.Tests
{
public class XmlTests
{
[Fact]
public void Can_Deserialize_Elements_On_Default_Root() {
var doc = CreateElementsXml();

var d = new XmlDeserializer();
var p = d.Deserialize<Person>(doc);

Assert.Equal("John Sheehan", p.Name);
Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
Assert.Equal(28, p.Age);
Assert.Equal(long.MaxValue, p.BigNumber);
Assert.Equal(99.9999m, p.Percent);
Assert.Equal(false, p.IsCool);

Assert.NotNull(p.Friends);
Assert.Equal(10, p.Friends.Count);

Assert.NotNull(p.BestFriend);
Assert.Equal("The Fonz", p.BestFriend.Name);
Assert.Equal(1952, p.BestFriend.Since);
}

[Fact]
public void Can_Deserialize_Attributes_On_Default_Root() {
var doc = CreateAttributesXml();

var d = new XmlDeserializer();
var p = d.Deserialize<Person>(doc);

Assert.Equal("John Sheehan", p.Name);
Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
Assert.Equal(28, p.Age);
Assert.Equal(long.MaxValue, p.BigNumber);
Assert.Equal(99.9999m, p.Percent);
Assert.Equal(false, p.IsCool);

Assert.NotNull(p.BestFriend);
Assert.Equal("The Fonz", p.BestFriend.Name);
Assert.Equal(1952, p.BestFriend.Since);
}

[Fact]
public void Ignore_Protected_Property_That_Exists_In_Data() {
var doc = CreateElementsXml();

var d = new XmlDeserializer();
var p = d.Deserialize<Person>(doc);

Assert.Null(p.IgnoreProxy);
}

[Fact]
public void Ignore_ReadOnly_Property_That_Exists_In_Data() {
var doc = CreateElementsXml();

var d = new XmlDeserializer();
var p = d.Deserialize<Person>(doc);

Assert.Null(p.ReadOnlyProxy);
}

[Fact]
public void Can_Deserialize_Names_With_Underscores_On_Default_Root() {
var doc = CreateUnderscoresXml();

var d = new XmlDeserializer();
var p = d.Deserialize<Person>(doc);

Assert.Equal("John Sheehan", p.Name);
Assert.Equal(new DateTime(2009, 9, 25, 0, 6, 1), p.StartDate);
Assert.Equal(28, p.Age);
Assert.Equal(long.MaxValue, p.BigNumber);
Assert.Equal(99.9999m, p.Percent);
Assert.Equal(false, p.IsCool);

Assert.NotNull(p.Friends);
Assert.Equal(10, p.Friends.Count);

Assert.NotNull(p.BestFriend);
Assert.Equal("The Fonz", p.BestFriend.Name);
Assert.Equal(1952, p.BestFriend.Since);

Assert.NotNull(p.Foes);
Assert.Equal(5, p.Foes.Count);
Assert.Equal("Yankees", p.Foes.Team);
}

private static string CreateUnderscoresXml() {
var doc = new XDocument();
var root = new XElement("Person");
root.Add(new XElement("Name", "John Sheehan"));
root.Add(new XElement("Start_Date", new DateTime(2009, 9, 25, 0, 6, 1)));
root.Add(new XAttribute("Age", 28));
root.Add(new XElement("Percent", 99.9999m));
root.Add(new XElement("Big_Number", long.MaxValue));
root.Add(new XAttribute("Is_Cool", false));
root.Add(new XElement("Ignore", "dummy"));
root.Add(new XAttribute("Read_Only", "dummy"));

root.Add(new XElement("Best_Friend",
new XElement("Name", "The Fonz"),
new XAttribute("Since", 1952)
));

var friends = new XElement("Friends");
for (int i = 0; i < 10; i++) {
friends.Add(new XElement("Friend",
new XElement("Name", "Friend" + i),
new XAttribute("Since", DateTime.Now.Year - i)
));
}
root.Add(friends);

var foes = new XElement("Foes");
foes.Add(new XAttribute("Team", "Yankees"));
for (int i = 0; i < 5; i++) {
foes.Add(new XElement("Foe", new XElement("Nickname", "Foe" + i)));
}
root.Add(foes);

doc.Add(root);
return doc.ToString();
}

private static string CreateElementsXml() {
var doc = new XDocument();
var root = new XElement("Person");
root.Add(new XElement("Name", "John Sheehan"));
root.Add(new XElement("StartDate", new DateTime(2009, 9, 25, 0, 6, 1)));
root.Add(new XElement("Age", 28));
root.Add(new XElement("Percent", 99.9999m));
root.Add(new XElement("BigNumber", long.MaxValue));
root.Add(new XElement("IsCool", false));
root.Add(new XElement("Ignore", "dummy"));
root.Add(new XElement("ReadOnly", "dummy"));

root.Add(new XElement("BestFriend",
new XElement("Name", "The Fonz"),
new XElement("Since", 1952)
));

var friends = new XElement("Friends");
for (int i = 0; i < 10; i++) {
friends.Add(new XElement("Friend",
new XElement("Name", "Friend" + i),
new XElement("Since", DateTime.Now.Year - i)
));
}
root.Add(friends);

doc.Add(root);
return doc.ToString();
}

private static string CreateAttributesXml() {
var doc = new XDocument();
var root = new XElement("Person");
root.Add(new XAttribute("Name", "John Sheehan"));
root.Add(new XAttribute("StartDate", new DateTime(2009, 9, 25, 0, 6, 1)));
root.Add(new XAttribute("Age", 28));
root.Add(new XAttribute("Percent", 99.9999m));
root.Add(new XAttribute("BigNumber", long.MaxValue));
root.Add(new XAttribute("IsCool", false));
root.Add(new XAttribute("Ignore", "dummy"));
root.Add(new XAttribute("ReadOnly", "dummy"));

root.Add(new XElement("BestFriend",
new XAttribute("Name", "The Fonz"),
new XAttribute("Since", 1952)
));

doc.Add(root);
return doc.ToString();
}
}
}
50 changes: 50 additions & 0 deletions RestSharp.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp.Example", "Stillwater.Example\RestSharp.Example.csproj", "{31C039A6-0824-494D-9100-E0C55D45D421}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FogBugzApi", "FogBugzApi\FogBugzApi.csproj", "{4B586EC1-3D01-4793-B749-7C50C26E51CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp", "RestSharp\RestSharp.csproj", "{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{45F37FD0-BD61-412C-90D5-ED99B1607478}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwilioApi", "TwilioApi\TwilioApi.csproj", "{894FF9E8-4DBF-4472-8AE2-1A6A988E105B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestSharp.Tests", "RestSharp.Tests\RestSharp.Tests.csproj", "{1464E4AC-18BB-4F23-8A0B-68196F9E1871}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{31C039A6-0824-494D-9100-E0C55D45D421}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{31C039A6-0824-494D-9100-E0C55D45D421}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31C039A6-0824-494D-9100-E0C55D45D421}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31C039A6-0824-494D-9100-E0C55D45D421}.Release|Any CPU.Build.0 = Release|Any CPU
{4B586EC1-3D01-4793-B749-7C50C26E51CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B586EC1-3D01-4793-B749-7C50C26E51CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B586EC1-3D01-4793-B749-7C50C26E51CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B586EC1-3D01-4793-B749-7C50C26E51CB}.Release|Any CPU.Build.0 = Release|Any CPU
{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}.Release|Any CPU.Build.0 = Release|Any CPU
{894FF9E8-4DBF-4472-8AE2-1A6A988E105B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{894FF9E8-4DBF-4472-8AE2-1A6A988E105B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{894FF9E8-4DBF-4472-8AE2-1A6A988E105B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{894FF9E8-4DBF-4472-8AE2-1A6A988E105B}.Release|Any CPU.Build.0 = Release|Any CPU
{1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1464E4AC-18BB-4F23-8A0B-68196F9E1871}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{4B586EC1-3D01-4793-B749-7C50C26E51CB} = {45F37FD0-BD61-412C-90D5-ED99B1607478}
{894FF9E8-4DBF-4472-8AE2-1A6A988E105B} = {45F37FD0-BD61-412C-90D5-ED99B1607478}
EndGlobalSection
EndGlobal
23 changes: 23 additions & 0 deletions RestSharp/Authenticators/HttpBasicAuthenticator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace RestSharp
{
public class HttpBasicAuthenticator : IAuthenticator
{
public string _username { get; set; }
public string _password { get; set; }

public HttpBasicAuthenticator(string Username, string Password) {
_password = Password;
_username = Username;
}

public void Authenticate(RestRequest request) {
request.Credentials = new NetworkCredential(_username, _password);
}
}
}
12 changes: 12 additions & 0 deletions RestSharp/Authenticators/IAuthenticator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RestSharp
{
public interface IAuthenticator
{
void Authenticate(RestRequest request);
}
}
27 changes: 27 additions & 0 deletions RestSharp/Authenticators/SimpleAuthenticator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RestSharp
{
public class SimpleAuthenticator : IAuthenticator
{
public string _usernameKey { get; set; }
public string _username { get; set; }
public string _passwordKey { get; set; }
public string _password { get; set; }

public SimpleAuthenticator(string UsernameKey, string Username, string PasswordKey, string Password) {
_password = Password;
_passwordKey = PasswordKey;
_username = Username;
_usernameKey = UsernameKey;
}

public void Authenticate(RestRequest request) {
request.AddParameter(_usernameKey, _username);
request.AddParameter(_passwordKey, _password);
}
}
}
11 changes: 11 additions & 0 deletions RestSharp/Deserializers/IDeserializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
namespace RestSharp.Deserializers
{
public interface IDeserializer
{
X Deserialize<X>(string content) where X : new();
string RootElement { get; set; }
string Namespace { get; set; }
DateFormat DateFormat { get; set; }
}
}
89 changes: 89 additions & 0 deletions RestSharp/Deserializers/JsonDeserializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Collections;
using Newtonsoft.Json.Linq;

namespace RestSharp.Deserializers
{
public class JsonDeserializer : IDeserializer
{
public string RootElement { get; set; }
public string Namespace { get; set; }
public DateFormat DateFormat { get; set; }

public X Deserialize<X>(string content) where X : new() {
var x = new X();

JObject json = JObject.Parse(content);
JToken root = json.Root;

if (RootElement.HasValue())
root = json[RootElement];

Map(x, root);

return x;
}

private void Map(object x, JToken json) {
var objType = x.GetType();
var props = objType.GetProperties();

foreach (var prop in props) {
var type = prop.PropertyType;

if (!type.IsPublic || !prop.CanWrite)
continue;

var name = prop.Name;
var value = json[name];

if (value == null)
continue;

if (type.IsPrimitive) {
prop.SetValue(x, Convert.ChangeType(value.ToString(), type), null);
}
else if (type == typeof(string)) {
string raw = value.ToString();
// remove leading and trailing "
prop.SetValue(x, raw.Substring(1, raw.Length - 2), null);
}
else if (type == typeof(DateTime)) {
var dt = value != null ? value.ToString().ParseJsonDate() : default(DateTime);
prop.SetValue(x, dt, null);
}
else if (type == typeof(Decimal)) {
var dec = value != null ? Decimal.Parse(value.ToString()) : default(decimal);
prop.SetValue(x, dec, null);
}
else if (type.IsGenericType) {
// TODO: handle Dictionaries

var t = type.GetGenericArguments()[0];
var list = (IList)Activator.CreateInstance(type);

var elements = value.Children();

foreach (var element in elements) {
var item = CreateAndMap(t, element);
list.Add(item);
}

prop.SetValue(x, list, null);
}
else {
// nested property classes
var item = CreateAndMap(type, json[name]);
prop.SetValue(x, item, null);
}
}
}

private object CreateAndMap(Type t, JToken element) {
var item = Activator.CreateInstance(t);
Map(item, element);
return item;
}

}
}
162 changes: 162 additions & 0 deletions RestSharp/Deserializers/XmlDeserializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Collections;

namespace RestSharp.Deserializers
{
public class XmlDeserializer : IDeserializer
{
public string RootElement { get; set; }
public string Namespace { get; set; }
public DateFormat DateFormat { get; set; }

public X Deserialize<X>(string content) where X : new() {
var x = new X();

var doc = XDocument.Parse(content);
var root = doc.Root;
if (RootElement.HasValue())
root = doc.Root.Element(GetNamespacedName(RootElement));

var objType = x.GetType();

if (objType.IsSubclassOfRawGeneric(typeof(List<>))) {
x = (X)HandleListDerivative(x, root, objType.Name, objType);
}
else {
Map(x, root);
}

return x;
}

private XName GetNamespacedName(string name) {
XName xName = name;

if (Namespace.HasValue())
xName = XName.Get(name, Namespace);

return xName;
}

private void Map(object x, XElement root) {
var objType = x.GetType();
var props = objType.GetProperties();

foreach (var prop in props) {
var type = prop.PropertyType;

if (!type.IsPublic || !prop.CanWrite)
continue;

var name = GetNamespacedName(prop.Name);
var value = GetValueFromXml(root, name);

if (value == null)
continue;

if (type.IsPrimitive) {
prop.SetValue(x, Convert.ChangeType(value, type), null);
}
else if (type == typeof(string)) {
prop.SetValue(x, value, null);
}
else if (type == typeof(DateTime)) {
value = value != null ? DateTime.Parse(value.ToString()) : default(DateTime);
prop.SetValue(x, value, null);
}
else if (type == typeof(Decimal)) {
value = value != null ? Decimal.Parse(value.ToString()) : default(decimal);
prop.SetValue(x, value, null);
}
else if (type.IsGenericType) {
var t = type.GetGenericArguments()[0];
var list = (IList)Activator.CreateInstance(type);

var elements = root.Descendants(GetNamespacedName(t.Name));

// TODO if elements is empty, do search for descendants names that match without underscores

foreach (var element in elements) {
var item = CreateAndMap(t, element);
list.Add(item);
}

prop.SetValue(x, list, null);
}
else if (type.IsSubclassOfRawGeneric(typeof(List<>))) {
// handles classes that derive from List<T>
// e.g. a collection that also has attributes
var list = HandleListDerivative(x, root, prop.Name, type);
prop.SetValue(x, list, null);
}
else {
// nested property classes
if (root != null) {
var element = root.Descendants().FirstOrDefault(d => d.Name.LocalName.RemoveUnderscores() == name.LocalName);
if (element != null) {
var item = CreateAndMap(type, element);
prop.SetValue(x, item, null);
}
}
}
}
}

private object HandleListDerivative(object x, XElement root, string propName, Type type) {
var t = type.BaseType.GetGenericArguments()[0]; // TODO: only works one level down

var list = (IList)Activator.CreateInstance(type);

var elements = root.Descendants(GetNamespacedName(t.Name));

foreach (var element in elements) {
var item = CreateAndMap(t, element);
list.Add(item);
}

// get properties too, not just list items
Map(list, root.Element(GetNamespacedName(propName)));

return list;
}

private object CreateAndMap(Type t, XElement element) {
var item = Activator.CreateInstance(t);
Map(item, element);
return item;
}

private object GetValueFromXml(XElement root, string name) {
return GetValueFromXml(root, XName.Get(name));
}

private object GetValueFromXml(XElement root, XName name) {
object val = null;

if (root != null) {
if (root.Element(name) != null) {
val = root.Element(name).Value;
}
else if (root.Attribute(name) != null) {
val = root.Attribute(name).Value;
}
else if (name == "Data" && root.Value != null) {
val = root.Value;
}
else {
// try looking for element that matches sanitized property name
var element = root.Descendants().FirstOrDefault(d => d.Name.LocalName.RemoveUnderscores() == name.LocalName);
if (element != null) {
val = element.Value;
}
}
}

return val;
}
}
}
43 changes: 43 additions & 0 deletions RestSharp/Enum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RestSharp
{
public enum ResponseFormat
{
Auto,
Json,
Xml,
None
}

public enum RequestFormat
{
Parameters,
Xml,
Json
}

public enum Method
{
GET,
POST,
PUT,
DELETE,
HEAD,
OPTIONS
}

public enum UrlMode
{
AsIs,
ReplaceValues
}

public enum DateFormat
{

}
}
77 changes: 77 additions & 0 deletions RestSharp/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Globalization;
using System.Text.RegularExpressions;

namespace RestSharp
{
public static class Extensions
{
public static bool HasValue(this string input) {
return !string.IsNullOrEmpty(input);
}

public static string RemoveUnderscores(this string input) {
return input.Replace("_", "");
}

public static string ReadAsString(this Stream stream) {
using (var reader = new StreamReader(stream)) {
return reader.ReadToEnd();
}
}

public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) {
while (toCheck != typeof(object)) {
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur) {
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}

public static DateTime ParseJsonDate(this string input) {
if (input.Contains("/Date(")) {
var regex = new Regex(@"\\/Date\((\d+)(-|\+)?([0-9]{4})?\)\\/");
if (regex.IsMatch(input)) {
var matches = regex.Matches(input);
var match = matches[0];
var ms = Convert.ToInt64(match.Groups[1].Value);
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var dt = epoch.AddMilliseconds(ms);

// adjust if time zone modifier present
if (match.Groups[3] != null) {
var mod = DateTime.ParseExact(match.Groups[3].Value, "hhmm", CultureInfo.InvariantCulture);
if (match.Groups[2].Value == "+") {
dt = dt.Add(mod.TimeOfDay);
}
else {
dt = dt.Subtract(mod.TimeOfDay);
}
}

return dt;
}
}
else if (input.Contains("new Date(")) {

}
else if (input.Matches(@"([0-9-])*T([0-9\:]*)Z")) {

}

return default(DateTime);
}

public static bool Matches(this string input, string pattern) {
return Regex.IsMatch(input, pattern);
}
}
}
126 changes: 126 additions & 0 deletions RestSharp/Http/Http.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using System.Web;
using System.IO;

namespace RestSharp
{
public class Http : IHttp
{
public ICredentials Credentials { get; set; }
public IDictionary<string, string> Headers { get; private set; }
public Http() {
Headers = new Dictionary<string, string>();
}

public RestResponse Post(Uri uri, IEnumerable<KeyValuePair<string, string>> @params) {
return Post(uri, @params, "application/x-www-form-urlencoded");
}

public RestResponse Post(Uri uri, IEnumerable<KeyValuePair<string, string>> @params, string contentType) {
return PostPutInternal(uri, @params, contentType, "POST");
}

public RestResponse Put(Uri uri, IEnumerable<KeyValuePair<string, string>> @params) {
return Put(uri, @params, "application/x-www-form-urlencoded");
}

public RestResponse Put(Uri uri, IEnumerable<KeyValuePair<string, string>> @params, string contentType) {
return PostPutInternal(uri, @params, contentType, "PUT");
}

private RestResponse PostPutInternal(Uri uri, IEnumerable<KeyValuePair<string, string>> @params, string contentType, string method) {
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = method;

if (this.Credentials != null) {
request.Credentials = this.Credentials;
}

if (@params.Count() > 1) {
var data = EncodeParameters(@params);
request.ContentLength = data.Length;
request.ContentType = contentType;

var requestStream = request.GetRequestStream();
using (StreamWriter writer = new StreamWriter(requestStream, Encoding.ASCII)) {
writer.Write(data);
}
}

AppendHeaders(request);
return GetResponse(request);
}

private string EncodeParameters(IEnumerable<KeyValuePair<string, string>> @params) {
var querystring = new StringBuilder();
foreach (var p in @params) {
if (querystring.Length > 1) querystring.Append("&");
querystring.AppendFormat("{0}={1}", HttpUtility.UrlEncode(p.Key), HttpUtility.UrlEncode(p.Value));
}

return querystring.ToString();
}

public RestResponse Get(Uri uri, IEnumerable<KeyValuePair<string, string>> @params) {
return GetStyleVerbInternal(uri, "GET", @params);
}

public RestResponse Head(Uri uri, IEnumerable<KeyValuePair<string, string>> @params) {
return GetStyleVerbInternal(uri, "HEAD", @params);
}

public RestResponse Options(Uri uri, IEnumerable<KeyValuePair<string, string>> @params) {
return GetStyleVerbInternal(uri, "OPTIONS", @params);
}

public RestResponse Delete(Uri uri, IEnumerable<KeyValuePair<string, string>> @params) {
return GetStyleVerbInternal(uri, "DELETE", @params);
}

private RestResponse GetStyleVerbInternal(Uri uri, string method, IEnumerable<KeyValuePair<string, string>> @params) {
string url = uri.ToString();
if (@params.Count() > 1) {
var data = EncodeParameters(@params);
url = string.Format("{0}?{1}", url, data);
}

var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = method;
if (this.Credentials != null) {
request.Credentials = this.Credentials;
}

AppendHeaders(request);
return GetResponse(request);
}

private void AppendHeaders(HttpWebRequest request) {
foreach (var header in Headers) {
request.Headers[header.Key] = header.Value;
}
}

private static RestResponse GetResponse(HttpWebRequest request) {
using (var raw = (HttpWebResponse)request.GetResponse()) {
var response = new RestResponse();
response.ContentType = raw.ContentType;
response.ContentLength = raw.ContentLength;
response.ContentEncoding = raw.ContentEncoding;
response.Content = raw.GetResponseStream().ReadAsString();
response.StatusCode = raw.StatusCode;
response.StatusDescription = raw.StatusDescription;
response.ResponseUri = raw.ResponseUri;
response.Server = raw.Server;

raw.Close();

return response;
}
}
}
}
22 changes: 22 additions & 0 deletions RestSharp/Http/IHttp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Net;

using RestSharp;

namespace RestSharp
{
public interface IHttp
{
ICredentials Credentials { get; set; }
RestResponse Delete(Uri uri, IEnumerable<KeyValuePair<string, string>> @params);
RestResponse Get(Uri uri, IEnumerable<KeyValuePair<string, string>> @params);
RestResponse Head(Uri uri, IEnumerable<KeyValuePair<string, string>> @params);
IDictionary<string, string> Headers { get; }
RestResponse Options(Uri uri, IEnumerable<KeyValuePair<string, string>> @params);
RestResponse Post(Uri uri, IEnumerable<KeyValuePair<string, string>> @params, string contentType);
RestResponse Post(Uri uri, IEnumerable<KeyValuePair<string, string>> @params);
RestResponse Put(Uri uri, IEnumerable<KeyValuePair<string, string>> @params);
RestResponse Put(Uri uri, IEnumerable<KeyValuePair<string, string>> @params, string contentType);
}
}
10 changes: 10 additions & 0 deletions RestSharp/IRestClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
namespace RestSharp
{
public interface IRestClient
{
IAuthenticator Authenticator { get; set; }
X Execute<X>(RestRequest request) where X : new();
RestResponse Execute(RestRequest request);
}
}
25 changes: 25 additions & 0 deletions RestSharp/Parameter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RestSharp
{
public class Parameter
{
public string Name { get; set; }
public object Value { get; set; }
public ParameterType Type { get; set; }

public override string ToString() {
return string.Format("{0}={1}", Name, Value);
}
}

public enum ParameterType
{
GetOrPost,
UrlSegment,
HttpHeader
}
}
36 changes: 36 additions & 0 deletions RestSharp/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RestSharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RestSharp")]
[assembly: AssemblyCopyright("Copyright © John Sheehan 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("d2b12a34-b748-47f9-8ad6-f84da992c64b")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
133 changes: 133 additions & 0 deletions RestSharp/RestClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using Newtonsoft.Json;
using System.Reflection;
using RestSharp.Deserializers;

namespace RestSharp
{
public class RestClient : IRestClient
{
private readonly IHttp _http;
private readonly IDeserializer _jsonDeserializer;
private readonly IDeserializer _xmlDeserializer;

public RestClient()
: this(new Http(), new JsonDeserializer(), new XmlDeserializer()) {
}

public RestClient(IHttp http, IDeserializer jsonDeserializer, IDeserializer xmlDeserializer) {
_http = http;
_jsonDeserializer = jsonDeserializer;
_xmlDeserializer = xmlDeserializer;
}

public IAuthenticator Authenticator { get; set; }

public RestResponse Execute(RestRequest request) {
Authenticator.Authenticate(request);
var response = GetResponse(request);
return response;
}

public X Execute<X>(RestRequest request) where X : new() {
Authenticator.Authenticate(request);

// make request
var response = GetResponse(request);

// handle response
X returnVal = default(X);

if (request.ResponseFormat == ResponseFormat.Auto) {
switch (request.ContentType) {
case "application/json":
request.ResponseFormat = ResponseFormat.Json;
break;
case "text/xml":
request.ResponseFormat = ResponseFormat.Xml;
break;
}
}

switch (request.ResponseFormat) {
case ResponseFormat.Json:
returnVal = DeserializeJsonTo<X>(response.Content);
break;
case ResponseFormat.Xml:
returnVal = DeserializeXmlTo<X>(response.Content, request.RootElement, request.XmlNamespace);
break;
}

return returnVal;
}

private RestResponse GetResponse(RestRequest request) {
if (request.Credentials != null) {
_http.Credentials = request.Credentials;
}

var headers = from p in request.Parameters
where p.Type == ParameterType.HttpHeader
select new {
Name = p.Name,
Value = p.Value.ToString()
};

foreach (var header in headers) {
_http.Headers.Add(header.Name, header.Value);
}

var @params = request.Parameters
.Where(p => p.Type == ParameterType.GetOrPost)
.ToDictionary(k => k.Name, e => e.Value.ToString());

var response = new RestResponse();

try {
switch (request.Verb) {
case Method.GET:
response = _http.Get(request.GetUri(), @params);
break;
case Method.POST:
response = _http.Post(request.GetUri(), @params);
break;
case Method.PUT:
response = _http.Put(request.GetUri(), @params);
break;
case Method.DELETE:
response = _http.Delete(request.GetUri(), @params);
break;
case Method.HEAD:
response = _http.Head(request.GetUri(), @params);
break;
case Method.OPTIONS:
response = _http.Options(request.GetUri(), @params);
break;
}
}
catch (Exception ex) {
// TODO: handle transport errors better
response = new RestResponse { Content = ex.Message };
}

return response;
}

private X DeserializeJsonTo<X>(string content) where X : new() {
var deserializer = new JsonDeserializer();
return deserializer.Deserialize<X>(content);
}

private X DeserializeXmlTo<X>(string content, string rootElement, string xmlNamespace) where X : new() {
_xmlDeserializer.Namespace = xmlNamespace;
_xmlDeserializer.RootElement = rootElement;
return _xmlDeserializer.Deserialize<X>(content);
}
}
}
154 changes: 154 additions & 0 deletions RestSharp/RestRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;

namespace RestSharp
{
public class RestRequest
{
public RestRequest() {
Initialize();
}

public RestRequest(Method verb)
: this() {
Verb = verb;
}

public RestRequest(string action)
: this() {
Action = action;
}

public RestRequest(string action, Method verb)
: this() {
Action = action;
Verb = verb;
}

private void Initialize() {
Parameters = new List<Parameter>();
}

public void AddObject(object obj, params string[] whitelist) {
// automatically create parameters from object props
var type = obj.GetType();
var props = type.GetProperties();

foreach (var prop in props) {
bool isAllowed = whitelist.Length > 0 && whitelist.Contains(prop.Name);

if (isAllowed) {
var propType = prop.PropertyType;
var val = prop.GetValue(obj, null);

if (val != null) {
if (propType.IsArray) {
val = string.Join(",", (string[])val);
}

AddParameter(prop.Name, val);
}
}
}
}

public void AddObject(object obj) {
AddObject(obj, string.Empty);
}

public RestRequest AddParameter(Parameter p) {
Parameters.Add(p);
return this;
}

public RestRequest AddParameter(string name, object value) {
return AddParameter(new Parameter { Name = name, Value = value, Type = ParameterType.GetOrPost });
}

public RestRequest AddParameter(string name, object value, ParameterType type) {
return AddParameter(new Parameter { Name = name, Value = value, Type = type });
}

public List<Parameter> Parameters { get; private set; }

private Method _verb = Method.GET;
public Method Verb {
get { return _verb; }
set { _verb = value; }
}

public string Action { get; set; }

private string _ActionFormat;
public string ActionFormat {
get {
return _ActionFormat;
}
set {
UrlMode = UrlMode.ReplaceValues;
_ActionFormat = value;
}
}

private UrlMode _UrlMode = UrlMode.AsIs;
private UrlMode UrlMode {
get {
return _UrlMode;
}
set {
_UrlMode = value;
}
}

public string BaseUrl { get; set; }
public string ContentType { get; set; }

private ResponseFormat _ResponseFormat = ResponseFormat.Auto;
public ResponseFormat ResponseFormat {
get {
return _ResponseFormat;
}
set {
_ResponseFormat = value;
}
}

private RequestFormat _RequestFormat = RequestFormat.Parameters;
public RequestFormat RequestFormat {
get {
return _RequestFormat;
}
set {
_RequestFormat = value;
}
}

public string RootElement { get; set; }
public string XmlNamespace { get; set; }
public ICredentials Credentials { get; set; }

public Uri GetUri() {
Uri url = null;

switch (UrlMode) {
case UrlMode.AsIs:
url = new Uri(string.Format("{0}/{1}", BaseUrl, Action));
break;
case UrlMode.ReplaceValues:
string assembled = this.ActionFormat;
var urlParms = Parameters.Where(p => p.Type == ParameterType.UrlSegment);
foreach (var p in urlParms) {
assembled = assembled.Replace("{" + p.Name + "}", p.Value.ToString());
}

url = new Uri(string.Format("{0}/{1}", BaseUrl, assembled));
break;
}

return url;
}
}
}
21 changes: 21 additions & 0 deletions RestSharp/RestResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;

namespace RestSharp
{
public class RestResponse
{
public string ContentType { get; set; }
public long ContentLength { get; set; }
public string ContentEncoding { get; set; }
public string Content { get; set; }
public HttpStatusCode StatusCode { get; set; }
public string StatusDescription { get; set; }
public Uri ResponseUri { get; set; }
public string Server { get; set; }
}
}
76 changes: 76 additions & 0 deletions RestSharp/RestSharp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{2ECECFBF-5F3E-40EE-A963-72336DC7ABE2}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RestSharp</RootNamespace>
<AssemblyName>RestSharp</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=3.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\References\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Deserializers\IDeserializer.cs" />
<Compile Include="Deserializers\JsonDeserializer.cs" />
<Compile Include="Deserializers\XmlDeserializer.cs" />
<Compile Include="Enum.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Authenticators\HttpBasicAuthenticator.cs" />
<Compile Include="Http\Http.cs" />
<Compile Include="Http\IHttp.cs" />
<Compile Include="IRestClient.cs" />
<Compile Include="RestResponse.cs" />
<Compile Include="Authenticators\IAuthenticator.cs" />
<Compile Include="Parameter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RestClient.cs" />
<Compile Include="RestRequest.cs" />
<Compile Include="Authenticators\SimpleAuthenticator.cs" />
<Compile Include="Validation\Require.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
5 changes: 5 additions & 0 deletions RestSharp/RestSharp.csproj.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ShowAllFiles</ProjectView>
</PropertyGroup>
</Project>
16 changes: 16 additions & 0 deletions RestSharp/Validation/Require.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RestSharp.Validation
{
public class Require
{
public static void Argument(string argumentName, object argumentValue) {
if (argumentValue == null) {
throw new ArgumentException("Argument cannot be null.", argumentName);
}
}
}
}