REST client API: Initial checkin (SPRNET-1345)

This commit is contained in:
bbaia
2010-07-28 00:09:16 +00:00
parent 46a0edbef8
commit ce275c9a25
52 changed files with 7775 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2008", "..\..\..\src\Spring\Spring.Core\Spring.Core.2008.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Http.2008", "..\..\..\src\Spring\Spring.Http\Spring.Http.2008.csproj", "{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Http.Tests.2008", "..\..\..\test\Spring\Spring.Http.Tests\Spring.Http.Tests.2008.csproj", "{F04CEE18-3897-A399-46BF-459437475B21}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.RestQuickStart.2008", "src\Spring.RestQuickStart\Spring.RestQuickStart.2008.csproj", "{5B47D309-31C9-4282-86E2-11FC63DFF55B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.Build.0 = Release|Any CPU
{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}.Release|Any CPU.Build.0 = Release|Any CPU
{F04CEE18-3897-A399-46BF-459437475B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F04CEE18-3897-A399-46BF-459437475B21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F04CEE18-3897-A399-46BF-459437475B21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F04CEE18-3897-A399-46BF-459437475B21}.Release|Any CPU.Build.0 = Release|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
NAntAddinLastFileName = Spring.build
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,40 @@
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Core.2010", "..\..\..\src\Spring\Spring.Core\Spring.Core.2010.csproj", "{710961A3-0DF4-49E4-A26E-F5B9C044AC84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Http.2010", "..\..\..\src\Spring\Spring.Http\Spring.Http.2010.csproj", "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.Http.Tests.2010", "..\..\..\test\Spring\Spring.Http.Tests\Spring.Http.Tests.2010.csproj", "{4594CEE7-3897-A3BF-9946-5B4374F01821}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spring.RestQuickStart.2010", "src\Spring.RestQuickStart\Spring.RestQuickStart.2010.csproj", "{5B47D309-31C9-4282-86E2-11FC63DFF55B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{710961A3-0DF4-49E4-A26E-F5B9C044AC84}.Release|Any CPU.Build.0 = Release|Any CPU
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}.Release|Any CPU.Build.0 = Release|Any CPU
{4594CEE7-3897-A3BF-9946-5B4374F01821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4594CEE7-3897-A3BF-9946-5B4374F01821}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4594CEE7-3897-A3BF-9946-5B4374F01821}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4594CEE7-3897-A3BF-9946-5B4374F01821}.Release|Any CPU.Build.0 = Release|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B47D309-31C9-4282-86E2-11FC63DFF55B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
NAntAddinLastFileName = Spring.build
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,29 @@
using System;
using Spring.Http.Rest;
namespace Spring.RestQuickStart
{
class Program
{
static void Main(string[] args)
{
try
{
RestTemplate rt = new RestTemplate("http://twitter.com");
string result = rt.GetForObject<string>("/statuses/user_timeline.xml?id={id}", "lancearmstrong");
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
Console.WriteLine("--- hit <return> to quit ---");
Console.ReadLine();
}
}
}
}

View File

@@ -0,0 +1,60 @@
<?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>{5B47D309-31C9-4282-86E2-11FC63DFF55B}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Spring.RestQuickStart</RootNamespace>
<AssemblyName>Spring.RestQuickStart</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="System" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Spring\Spring.Core\Spring.Core.2008.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2008</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\..\src\Spring\Spring.Http\Spring.Http.2008.csproj">
<Project>{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}</Project>
<Name>Spring.Http.2008</Name>
</ProjectReference>
</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>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" 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>{5B47D309-31C9-4282-86E2-11FC63DFF55B}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Spring.RestQuickStart</RootNamespace>
<AssemblyName>Spring.RestQuickStart</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation />
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</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="System" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\src\Spring\Spring.Core\Spring.Core.2010.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2010</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\..\src\Spring\Spring.Http\Spring.Http.2010.csproj">
<Project>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</Project>
<Name>Spring.Http.2010</Name>
</ProjectReference>
</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>

View File

@@ -0,0 +1,5 @@
using System;
using System.Reflection;
[assembly: AssemblyTitle("Spring.Http")]
[assembly: AssemblyDescription("Interfaces and classes that provide REST client API in Spring.Net")]

View File

@@ -0,0 +1,224 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
using Spring.Util;
namespace Spring.Http.Converters
{
/**
* Abstract base class for most {@link HttpMessageConverter} implementations.
*
* <p>This base class adds support for setting supported {@code MediaTypes}, through the
* {@link #setSupportedMediaTypes(List) supportedMediaTypes} bean property. It also adds
* support for {@code Content-Type} and {@code Content-Length} when writing to output messages.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
*/
public abstract class AbstractHttpMessageConverter : IHttpMessageConverter
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(AbstractHttpMessageConverter));
#endregion
private IList<MediaType> _supportedMediaTypes = new List<MediaType>();
/**
* Set the list of {@link MediaType} objects supported by this converter.
*/
public IList<MediaType> SupportedMediaTypes
{
get { return _supportedMediaTypes; }
set { _supportedMediaTypes = value; }
}
#region Constructor(s)
/**
* Construct an {@code AbstractHttpMessageConverter} with no supported media types.
* @see #setSupportedMediaTypes
*/
protected AbstractHttpMessageConverter()
{
}
/**
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
* @param supportedMediaTypes the supported media types
*/
protected AbstractHttpMessageConverter(params MediaType[] supportedMediaTypes)
{
this._supportedMediaTypes = new List<MediaType>(supportedMediaTypes);
}
#endregion
#region IHttpMessageConverter Membres
/**
* {@inheritDoc}
* <p>This implementation checks if the given class is {@linkplain #supports(Class) supported},
* and if the {@linkplain #getSupportedMediaTypes() supported media types}
* {@linkplain MediaType#includes(MediaType) include} the given media type.
*/
public bool CanRead(Type type, MediaType mediaType)
{
return Supports(type) && CanRead(mediaType);
}
/**
* {@inheritDoc}
* <p>This implementation checks if the given class is {@linkplain #supports(Class) supported},
* and if the {@linkplain #getSupportedMediaTypes() supported media types}
* {@linkplain MediaType#includes(MediaType) include} the given media type.
*/
public bool CanWrite(Type type, MediaType mediaType)
{
return Supports(type) && CanWrite(mediaType);
}
/**
* {@inheritDoc}
* <p>This implementation simple delegates to {@link #readInternal(Class, HttpInputMessage)}.
* Future implementations might add some default behavior, however.
*/
public T Read<T>(HttpWebResponse response) where T : class
{
return ReadInternal<T>(response);
}
/**
* {@inheritDoc}
* <p>This implementation delegates to {@link #getDefaultContentType(Object)} if a content
* type was not provided, calls {@link #getContentLength}, and sets the corresponding headers
* on the output message. It then calls {@link #writeInternal}.
*/
public void Write(object content, MediaType mediaType, HttpWebRequest request)
{
if (!StringUtils.HasText(request.Headers[HttpRequestHeader.ContentType]))
{
if (mediaType == null || mediaType.IsWildcardType || mediaType.IsWildcardSubtype)
{
mediaType = GetDefaultContentType(content.GetType());
}
if (mediaType != null)
{
request.ContentType = mediaType.ToString();
}
}
WriteInternal(content, request);
}
#endregion
/**
* Returns true if any of the {@linkplain #setSupportedMediaTypes(List) supported media types}
* include the given media type.
* @param mediaType the media type to read, can be {@code null} if not specified. Typically the value of a
* {@code Content-Type} header.
* @return true if the supported media types include the media type, or if the media type is {@code null}
*/
protected bool CanRead(MediaType mediaType)
{
if (mediaType == null)
{
return true;
}
foreach(MediaType supportedMediaType in this._supportedMediaTypes)
{
if (supportedMediaType.Includes(mediaType))
{
return true;
}
}
return false;
}
/**
* Returns true if the given media type includes any of the
* {@linkplain #setSupportedMediaTypes(List) supported media types}.
* @param mediaType the media type to write, can be {@code null} if not specified. Typically the value of an
* {@code Accept} header.
* @return true if the supported media types are compatible with the media type, or if the media type is {@code null}
*/
protected bool CanWrite(MediaType mediaType)
{
if (mediaType == null || mediaType.Equals(MediaType.ALL))
{
return true;
}
foreach(MediaType supportedMediaType in this._supportedMediaTypes)
{
if (supportedMediaType.IsCompatibleWith(mediaType))
{
return true;
}
}
return false;
}
/**
* Returns the default content type for the given type. Called when {@link #write}
* is invoked without a specified content type parameter.
* <p>By default, this returns the first element of the
* {@link #setSupportedMediaTypes(List) supportedMediaTypes} property, if any.
* Can be overridden in subclasses.
* @param t the type to return the content type for
* @return the content type, or <code>null</code> if not known
*/
protected virtual MediaType GetDefaultContentType(Type type)
{
return (this._supportedMediaTypes.Count > 0 ? this._supportedMediaTypes[0] : null);
}
/**
* Indicates whether the given class is supported by this converter.
* @param clazz the class to test for support
* @return <code>true</code> if supported; <code>false</code> otherwise
*/
protected abstract bool Supports(Type type);
/**
* Abstract template method that reads the actualy object. Invoked from {@link #read}.
* @param clazz the type of object to return
* @param inputMessage the HTTP input message to read from
* @return the converted object
* @throws IOException in case of I/O errors
* @throws HttpMessageNotReadableException in case of conversion errors
*/
protected abstract T ReadInternal<T>(HttpWebResponse response) where T : class;
/**
* Abstract template method that writes the actual body. Invoked from {@link #write}.
* @param t the object to write to the output message
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
protected abstract void WriteInternal(object content, HttpWebRequest request);
}
}

View File

@@ -0,0 +1,75 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
namespace Spring.Http.Converters
{
/**
* Implementation of {@link HttpMessageConverter} that can read and write byte arrays.
*
* <p>By default, this converter supports all media types (<code>&#42;&#47;&#42;</code>), and writes with a {@code
* Content-Type} of {@code application/octet-stream}. This can be overridden by setting the {@link
* #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class ByteArrayHttpMessageConverter : AbstractHttpMessageConverter
{
/** Creates a new instance of the {@code ByteArrayHttpMessageConverter}. */
public ByteArrayHttpMessageConverter() :
base(new MediaType("application", "octet-stream"), MediaType.ALL)
{
}
protected override bool Supports(Type type)
{
return type.Equals(typeof(byte[]));
}
protected override T ReadInternal<T>(HttpWebResponse response)
{
// Get the response stream
using (BinaryReader reader = new BinaryReader(response.GetResponseStream()))
{
return reader.ReadBytes((int)response.ContentLength) as T;
}
}
protected override void WriteInternal(object content, HttpWebRequest request)
{
// Create a byte array of the data we want to send
byte[] byteData = content as byte[];
// Set the content length in the request headers
request.ContentLength = byteData.Length;
// Write data
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
}
}
}
}

View File

@@ -0,0 +1,63 @@
#if NET_3_5
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Xml;
using System.ServiceModel.Syndication;
using Spring.Http.Converters.Xml;
namespace Spring.Http.Converters.Feed
{
public abstract class AbstractFeedHttpMessageConverter : AbstractXmlHttpMessageConverter
{
/**
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
* @param supportedMediaTypes the supported media types
*/
protected AbstractFeedHttpMessageConverter(params MediaType[] supportedMediaTypes) :
base(supportedMediaTypes)
{
}
protected override bool Supports(Type type)
{
return type.Equals(typeof(SyndicationFeed));
}
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
return SyndicationFeed.Load(xmlReader) as T;
}
protected override XmlReaderSettings GetDefaultXmlReaderSettings()
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.CloseInput = true;
settings.IgnoreProcessingInstructions = true;
settings.ProhibitDtd = false;
settings.XmlResolver = null;
return settings;
}
}
}
#endif

View File

@@ -0,0 +1,43 @@
#if NET_3_5
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Xml;
using System.ServiceModel.Syndication;
namespace Spring.Http.Converters.Feed
{
public class Atom10FeedHttpMessageConverter : AbstractFeedHttpMessageConverter
{
public Atom10FeedHttpMessageConverter() :
base(new MediaType("application", "atom+xml"))
{
}
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
SyndicationFeed rssFeed = content as SyndicationFeed;
rssFeed.SaveAsAtom10(xmlWriter);
}
}
}
#endif

View File

@@ -0,0 +1,43 @@
#if NET_3_5
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Xml;
using System.ServiceModel.Syndication;
namespace Spring.Http.Converters.Feed
{
public class Rss20FeedHttpMessageConverter : AbstractFeedHttpMessageConverter
{
public Rss20FeedHttpMessageConverter() :
base(new MediaType("application", "rss+xml"))
{
}
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
SyndicationFeed rssFeed = content as SyndicationFeed;
rssFeed.SaveAsRss20(xmlWriter);
}
}
}
#endif

View File

@@ -0,0 +1,85 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
namespace Spring.Http.Converters
{
/**
* Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
*/
public interface IHttpMessageConverter
{
/**
* Indicates whether the given class can be read by this converter.
* @param clazz the class to test for readability
* @param mediaType the media type to read, can be {@code null} if not specified. Typically the value of a
* {@code Content-Type} header.
* @return {@code true} if readable; {@code false} otherwise
*/
bool CanRead(Type type, MediaType mediaType);
/**
* Indicates whether the given class can be written by this converter.
* @param clazz the class to test for writability
* @param mediaType the media type to write, can be {@code null} if not specified. Typically the value of an
* {@code Accept} header.
* @return {@code true} if writable; {@code false} otherwise
*/
bool CanWrite(Type type, MediaType mediaType);
/**
* Return the list of {@link MediaType} objects supported by this converter.
* @return the list of supported media types
*/
IList<MediaType> SupportedMediaTypes { get; }
/**
* Read an object of the given type form the given input message, and returns it.
* @param clazz the type of object to return. This type must have previously been passed to the
* {@link #canRead canRead} method of this interface, which must have returned {@code true}.
* @param inputMessage the HTTP input message to read from
* @return the converted object
* @throws IOException in case of I/O errors
* @throws HttpMessageNotReadableException in case of conversion errors
*/
T Read<T>(HttpWebResponse response) where T : class;
/**
* Write an given object to the given output message.
* @param t the object to write to the output message. The type of this object must have previously been
* passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
* @param contentType the content type to use when writing. May be {@code null} to indicate that the
* default content type of the converter must be used. If not {@code null}, this media type must have
* previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
* returned {@code true}.
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
void Write(object content, MediaType mediaType, HttpWebRequest request);
}
}

View File

@@ -0,0 +1,89 @@
#if NET_3_5
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Xml;
using System.IO;
using System.Net;
using System.Text;
using System.Runtime.Serialization.Json;
using Spring.Util;
namespace Spring.Http.Converters.Json
{
// TODO : Support for known types, etc...
public class JsonHttpMessageConverter : AbstractHttpMessageConverter
{
public static readonly Encoding DEFAULT_CHARSET = Encoding.UTF8;
public JsonHttpMessageConverter() :
base(new MediaType("application", "json"))
{
}
protected override bool Supports(Type type)
{
return true;
}
protected override T ReadInternal<T>(HttpWebResponse response)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
using (Stream stream = response.GetResponseStream())
{
return (T)serializer.ReadObject(stream) as T;
}
}
protected override void WriteInternal(object content, HttpWebRequest request)
{
// Get the request encoding
MediaType mediaType = MediaType.ParseMediaType(request.Headers[HttpRequestHeader.ContentType]);
Encoding encoding;
if (mediaType == null || String.IsNullOrEmpty(mediaType.CharSet))
{
encoding = DEFAULT_CHARSET;
}
else
{
encoding = Encoding.GetEncoding(mediaType.CharSet);
}
DataContractJsonSerializer serializer = new DataContractJsonSerializer(content.GetType());
// Write data
using (Stream postStream = request.GetRequestStream())
{
using (XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(postStream, encoding, false))
{
serializer.WriteObject(jsonWriter, content);
jsonWriter.Flush();
}
postStream.Flush();
// Set the content length in the request headers
request.ContentLength = postStream.Length;
}
}
}
}
#endif

View File

@@ -0,0 +1,99 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
namespace Spring.Http.Converters
{
/**
* Implementation of {@link HttpMessageConverter} that can read and write strings.
*
* <p>By default, this converter supports all media types (<code>&#42;&#47;&#42;</code>), and writes with a {@code
* Content-Type} of {@code text/plain}. This can be overridden by setting the {@link
* #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class StringHttpMessageConverter : AbstractHttpMessageConverter
{
public static readonly Encoding DEFAULT_CHARSET = Encoding.GetEncoding("ISO-8859-1");
public StringHttpMessageConverter() :
base(new MediaType("text", "plain", "ISO-8859-1"), MediaType.ALL)
{
}
protected override bool Supports(Type type)
{
return type.Equals(typeof(string));
}
protected override T ReadInternal<T>(HttpWebResponse response)
{
// Get the response encoding
Encoding encoding;
if (String.IsNullOrEmpty(response.CharacterSet))
{
encoding = DEFAULT_CHARSET;
}
else
{
encoding = Encoding.GetEncoding(response.CharacterSet);
}
// Get the response stream
using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
{
return reader.ReadToEnd() as T;
}
}
protected override void WriteInternal(object content, HttpWebRequest request)
{
// Get the request encoding
MediaType mediaType = MediaType.ParseMediaType(request.Headers[HttpRequestHeader.ContentType]);
Encoding encoding;
if (mediaType == null || String.IsNullOrEmpty(mediaType.CharSet))
{
encoding = DEFAULT_CHARSET;
}
else
{
encoding = Encoding.GetEncoding(mediaType.CharSet);
}
// Create a byte array of the data we want to send
byte[] byteData = encoding.GetBytes(content as string);
// Set the content length in the request headers
request.ContentLength = byteData.Length;
// Write data
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
}
}
}
}

View File

@@ -0,0 +1,110 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Xml;
using System.Net;
using System.Text;
namespace Spring.Http.Converters.Xml
{
public abstract class AbstractXmlHttpMessageConverter : AbstractHttpMessageConverter
{
public static readonly Encoding DEFAULT_CHARSET = Encoding.UTF8;
private XmlReaderSettings _xmlReaderSettings;
public XmlReaderSettings XmlReaderSettings
{
get
{
if (_xmlReaderSettings == null)
{
_xmlReaderSettings = this.GetDefaultXmlReaderSettings();
}
return _xmlReaderSettings;
}
set { _xmlReaderSettings = value; }
}
/**
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
* @param supportedMediaTypes the supported media types
*/
protected AbstractXmlHttpMessageConverter(params MediaType[] supportedMediaTypes) :
base(supportedMediaTypes)
{
}
protected override T ReadInternal<T>(HttpWebResponse response)
{
using (Stream stream = response.GetResponseStream())
{
using (XmlReader xmlReader = XmlReader.Create(stream, this.XmlReaderSettings))
{
return ReadXml<T>(xmlReader, response);
}
}
}
protected override void WriteInternal(object content, HttpWebRequest request)
{
// Get the request encoding
MediaType mediaType = MediaType.ParseMediaType(request.Headers[HttpRequestHeader.ContentType]);
Encoding encoding;
if (mediaType == null || String.IsNullOrEmpty(mediaType.CharSet))
{
encoding = DEFAULT_CHARSET;
}
else
{
encoding = Encoding.GetEncoding(mediaType.CharSet);
}
using (Stream postStream = request.GetRequestStream())
{
using (XmlTextWriter xmlWriter = new XmlTextWriter(postStream, encoding))
{
WriteXml(xmlWriter, content, request);
xmlWriter.Flush();
}
// TODO : Don't work
// Set the content length in the request headers
request.ContentLength = postStream.Length;
}
}
protected virtual XmlReaderSettings GetDefaultXmlReaderSettings()
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.CloseInput = true;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreWhitespace = true;
return settings;
}
protected abstract T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response) where T : class;
protected abstract void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request);
}
}

View File

@@ -0,0 +1,93 @@
#if NET_3_0
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using System.Xml;
using System.Text;
using System.Runtime.Serialization;
using Spring.Util;
namespace Spring.Http.Converters.Xml
{
// TODO : Derive from AbstractXmlHttpMessageConverter ?
// TODO : Support for known types, etc...
public class DataContractHttpMessageConverter : AbstractHttpMessageConverter
{
public static readonly Encoding DEFAULT_CHARSET = Encoding.UTF8;
public DataContractHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
{
}
protected override bool Supports(Type type)
{
return true;
//return (
// AttributeUtils.FindAttribute(type, typeof(DataContractAttribute)) != null ||
// AttributeUtils.FindAttribute(type, typeof(SerializableAttribute)) != null ||
// typeof(ISerializable).IsAssignableFrom(type));
}
protected override T ReadInternal<T>(HttpWebResponse response)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
using (Stream stream = response.GetResponseStream())
{
return (T)serializer.ReadObject(stream) as T;
}
}
protected override void WriteInternal(object content, HttpWebRequest request)
{
// Get the request encoding
MediaType mediaType = MediaType.ParseMediaType(request.Headers[HttpRequestHeader.ContentType]);
Encoding encoding;
if (mediaType == null || String.IsNullOrEmpty(mediaType.CharSet))
{
encoding = DEFAULT_CHARSET;
}
else
{
encoding = Encoding.GetEncoding(mediaType.CharSet);
}
DataContractSerializer serializer = new DataContractSerializer(content.GetType());
// Write data
using (Stream postStream = request.GetRequestStream())
{
using (XmlTextWriter xmlWriter = new XmlTextWriter(postStream, encoding))
{
serializer.WriteObject(xmlWriter, content);
xmlWriter.Flush();
}
// Set the content length in the request headers
request.ContentLength = postStream.Length;
}
}
}
}
#endif

View File

@@ -0,0 +1,54 @@
#if NET_3_5
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Xml;
using System.Xml.Linq;
namespace Spring.Http.Converters.Xml
{
// TODO : Support XElement.Load options
public class XElementHttpMessageConverter : AbstractXmlHttpMessageConverter
{
public XElementHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
{
}
protected override bool Supports(Type type)
{
return type.Equals(typeof(XElement));
}
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
return XElement.Load(xmlReader) as T;
}
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
XElement xElement = content as XElement;
xElement.WriteTo(xmlWriter);
}
}
}
#endif

View File

@@ -0,0 +1,52 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Xml;
using System.Net;
namespace Spring.Http.Converters.Xml
{
public class XmlDocumentHttpMessageConverter : AbstractXmlHttpMessageConverter
{
public XmlDocumentHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
{
}
protected override bool Supports(Type type)
{
return type.Equals(typeof(XmlDocument));
}
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
XmlDocument document = new XmlDocument();
document.Load(xmlReader);
return document as T;
}
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
XmlDocument document = content as XmlDocument;
document.WriteTo(xmlWriter);
}
}
}

View File

@@ -0,0 +1,58 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Xml;
using System.Net;
using System.Xml.Serialization;
using Spring.Util;
namespace Spring.Http.Converters.Xml
{
// TODO : Support for known types, etc...
public class XmlSerializableHttpMessageConverter : AbstractXmlHttpMessageConverter
{
public XmlSerializableHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
{
}
protected override bool Supports(Type type)
{
return true;
//return (
// AttributeUtils.FindAttribute(type, typeof(XmlRootAttribute)) != null ||
// AttributeUtils.FindAttribute(type, typeof(XmlTypeAttribute)) != null);
}
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return serializer.Deserialize(xmlReader) as T;
}
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
XmlSerializer serializer = new XmlSerializer(content.GetType());
serializer.Serialize(xmlWriter, content);
}
}
}

View File

@@ -0,0 +1,116 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;
namespace Spring.Http
{
/**
* Factory for {@link ClientHttpRequest} objects.
* Requests are created by the {@link #createRequest(URI, HttpMethod)} method.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class DefaultHttpWebRequestFactory : IHttpWebRequestFactory
{
// TODO : Add other properties
private X509CertificateCollection _clientCertificates;
private ICredentials _credentials;
private IWebProxy _proxy;
private int? _timeout;
public X509CertificateCollection ClientCertificates
{
get
{
if (this._clientCertificates == null)
{
this._clientCertificates = new X509CertificateCollection();
}
return this._clientCertificates;
}
}
public ICredentials Credentials
{
get { return _credentials; }
set { _credentials = value; }
}
public IWebProxy Proxy
{
get { return _proxy; }
set { _proxy = value; }
}
public int? Timeout
{
get { return _timeout; }
set { _timeout = value; }
}
#region IHttpWebRequestFactory Membres
/**
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method.
* <p>The returned request can be written to, and then executed by calling
* {@link ClientHttpRequest#execute()}.
* @param uri the URI to create a request for
* @param httpMethod the HTTP method to execute
* @return the created request
* @throws IOException in case of I/O errors
*/
public HttpWebRequest CreateRequest(Uri uri)
{
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
if (this._clientCertificates != null)
{
foreach (X509Certificate2 certificate in this._clientCertificates)
{
request.ClientCertificates.Add(certificate);
}
}
if (this._credentials != null)
{
request.Credentials = this._credentials;
}
if (this._proxy != null)
{
request.Proxy = this._proxy;
}
if (this._timeout != null)
{
request.Timeout = this._timeout.Value;
}
return request;
}
#endregion
}
}

View File

@@ -0,0 +1,44 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
namespace Spring.Http
{
/**
* Java 5 enumeration of HTTP request methods. Intended for use
* with {@link org.springframework.http.client.ClientHttpRequest}
* and {@link org.springframework.web.client.RestTemplate}.
*
* @author Arjen Poutsma
* @since 3.0
*/
public enum HttpMethod
{
OPTIONS,
GET,
HEAD,
POST,
PUT,
DELETE,
TRACE,
CONNECT
}
}

View File

@@ -0,0 +1,134 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System.Net;
namespace Spring.Http
{
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5
public class HttpRequestMessage
{
//private string requestUri;
//private string httpVersion;
private HttpMethod method;
private WebHeaderCollection headers;
private object body;
//public string RequestUri
//{
// get { return this.requestUri; }
// set { this.requestUri = value; }
//}
//public string HttpVersion
//{
// get { return httpVersion; }
// set { httpVersion = value; }
//}
public HttpMethod Method
{
get { return this.method; }
set { this.method = value; }
}
/**
* Returns the headers of this message.
*/
public WebHeaderCollection Headers
{
get { return this.headers; }
}
/**
* Returns the body of this message.
*/
public object Body
{
get { return this.body; }
}
/**
* Create a new {@code HttpRequestMessage} with no body and no headers.
*/
public HttpRequestMessage(HttpMethod method) :
this(null, new WebHeaderCollection(), method)
{
}
/**
* Create a new {@code HttpRequestMessage} with the given headers and no body.
* @param headers the message headers
*/
public HttpRequestMessage(WebHeaderCollection headers) :
this(null, headers, HttpMethod.GET)
{
}
/**
* Create a new {@code HttpRequestMessage} with the given headers and no body.
* @param headers the message headers
*/
public HttpRequestMessage(WebHeaderCollection headers, HttpMethod method) :
this(null, headers, method)
{
}
/**
* Create a new {@code HttpRequestMessage} with the given body and no headers.
* @param body the message body
*/
public HttpRequestMessage(object body) :
this(body, new WebHeaderCollection(), HttpMethod.GET)
{
}
/**
* Create a new {@code HttpRequestMessage} with the given body and no headers.
* @param body the message body
*/
public HttpRequestMessage(object body, HttpMethod method) :
this(body, new WebHeaderCollection(), method)
{
}
/**
* Create a new {@code HttpRequestMessage} with the given body and headers.
* @param body the messagae body
* @param headers the message headers
*/
public HttpRequestMessage(object body, WebHeaderCollection headers) :
this(body, headers, HttpMethod.GET)
{
}
/**
* Create a new {@code HttpRequestMessage} with the given body and headers.
* @param body the messagae body
* @param headers the message headers
*/
public HttpRequestMessage(object body, WebHeaderCollection headers, HttpMethod method)
{
this.method = method;
this.body = body;
this.headers = headers;
}
}
}

View File

@@ -0,0 +1,107 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System.Net;
namespace Spring.Http
{
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
public class HttpResponseMessage<T> where T : class
{
private WebHeaderCollection headers;
private T body;
private HttpStatusCode statusCode;
private string statusDescription;
/**
* Returns the headers of this entity.
*/
public WebHeaderCollection Headers
{
get { return this.headers; }
}
/**
* Returns the body of this entity.
*/
public T Body
{
get { return this.body; }
}
/**
* Return the HTTP status code of the response.
* @return the HTTP status as an HttpStatus enum value
*/
public HttpStatusCode StatusCode
{
get { return statusCode; }
}
public string StatusDescription
{
get { return statusDescription; }
}
/**
* Create a new {@code ResponseEntity} with the given status code, and no body, no headers.
* @param body the entity body
* @param statusCode the status code
*/
public HttpResponseMessage(HttpStatusCode statusCode, string statusDescription) :
this(null, null, statusCode, statusDescription)
{
}
/**
* Create a new {@code ResponseEntity} with the given body and status code, and no headers.
* @param body the entity body
* @param statusCode the status code
*/
public HttpResponseMessage(T body, HttpStatusCode statusCode, string statusDescription) :
this(body, null, statusCode, statusDescription)
{
}
/**
* Create a new {@code HttpEntity} with the given headers and status code, and no body.
* @param headers the entity headers
* @param statusCode the status code
*/
public HttpResponseMessage(WebHeaderCollection headers, HttpStatusCode statusCode, string statusDescription) :
this(null, headers, statusCode, statusDescription)
{
}
/**
* Create a new {@code HttpEntity} with the given body, headers, and status code.
* @param body the entity body
* @param headers the entity headers
* @param statusCode the status code
*/
public HttpResponseMessage(T body, WebHeaderCollection headers, HttpStatusCode statusCode, string statusDescription)
{
this.statusCode = statusCode;
this.statusDescription = statusDescription;
this.body = body;
this.headers = headers;
}
}
}

View File

@@ -0,0 +1,46 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
namespace Spring.Http
{
/**
* Factory for {@link ClientHttpRequest} objects.
* Requests are created by the {@link #createRequest(URI, HttpMethod)} method.
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface IHttpWebRequestFactory
{
/**
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method.
* <p>The returned request can be written to, and then executed by calling
* {@link ClientHttpRequest#execute()}.
* @param uri the URI to create a request for
* @param httpMethod the HTTP method to execute
* @return the created request
* @throws IOException in case of I/O errors
*/
HttpWebRequest CreateRequest(Uri uri);
}
}

View File

@@ -0,0 +1,790 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Text;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using Spring.Util;
namespace Spring.Http
{
/**
* Represents an Internet Media Type, as defined in the HTTP specification.
*
* <p>Consists of a {@linkplain #getType() type} and a {@linkplain #getSubtype() subtype}.
* Also has functionality to parse media types from a string using {@link #parseMediaType(String)},
* or multiple comma-separated media types using {@link #parseMediaTypes(String)}.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
* @see <a href="http://tools.ietf.org/html/rfc2616#section-3.7">HTTP 1.1, section 3.7</a>
*/
public class MediaType : IComparable<MediaType>
{
/**
* Public constant media type that includes all media ranges (i.e. <code>&#42;/&#42;</code>).
*/
public static readonly MediaType ALL = new MediaType("*", "*");
/**
* Public constant media type for {@code application/atom+xml}.
*/
public static readonly MediaType APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
/**
* Public constant media type for {@code application/x-www-form-urlencoded}.
* */
public static readonly MediaType APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
/**
* Public constant media type for {@code application/json}.
* */
public static readonly MediaType APPLICATION_JSON = new MediaType("application", "json");
/**
* Public constant media type for {@code application/octet-stream}.
* */
public static readonly MediaType APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
/**
* Public constant media type for {@code application/xhtml+xml}.
* */
public static readonly MediaType APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
/**
* Public constant media type for {@code image/gif}.
*/
public static readonly MediaType IMAGE_GIF = new MediaType("image", "gif");
/**
* Public constant media type for {@code image/jpeg}.
*/
public static readonly MediaType IMAGE_JPEG = new MediaType("image", "jpeg");
/**
* Public constant media type for {@code image/png}.
*/
public static readonly MediaType IMAGE_PNG = new MediaType("image", "png");
/**
* Public constant media type for {@code image/xml}.
*/
public static readonly MediaType APPLICATION_XML = new MediaType("application", "xml");
/**
* Public constant media type for {@code multipart/form-data}.
* */
public static readonly MediaType MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
/**
* Public constant media type for {@code text/html}.
* */
public static readonly MediaType TEXT_HTML = new MediaType("text", "html");
/**
* Public constant media type for {@code text/plain}.
* */
public static readonly MediaType TEXT_PLAIN = new MediaType("text", "plain");
/**
* Public constant media type for {@code text/xml}.
* */
public static readonly MediaType TEXT_XML = new MediaType("text", "xml");
private const string WILDCARD_TYPE = "*";
private const string PARAM_QUALITY_FACTOR = "q";
private const string PARAM_CHARSET = "charset";
private string type;
private string subtype;
private IDictionary<string, string> parameters;
/**
* Return the primary type.
*/
public string Type
{
get { return this.type; }
}
/**
* Return the subtype.
*/
public string Subtype
{
get { return this.subtype; }
}
/**
* Indicate whether the {@linkplain #getType() type} is the wildcard character <code>&#42;</code> or not.
*/
public bool IsWildcardType
{
get { return WILDCARD_TYPE == type; }
}
/**
* Indicate whether the {@linkplain #getSubtype() subtype} is the wildcard character <code>&#42;</code> or not.
* @return whether the subtype is <code>&#42;</code>
*/
public bool IsWildcardSubtype
{
get { return WILDCARD_TYPE == subtype; }
}
/**
* Return the character set, as indicated by a <code>charset</code> parameter, if any.
* @return the character set; or <code>null</code> if not available
*/
public string CharSet
{
get
{
string charSet = null;
this.parameters.TryGetValue(PARAM_CHARSET, out charSet);
return charSet;
//string charSet = this.parameters[PARAM_CHARSET];
//return (charSet != null ? Charset.forName(charSet) : null);
}
}
/**
* Return the quality value, as indicated by a <code>q</code> parameter, if any.
* Defaults to <code>1.0</code>.
* @return the quality factory
*/
public double QualityValue
{
get
{
string qualityFactory = null;
return this.parameters.TryGetValue(PARAM_QUALITY_FACTOR, out qualityFactory)
? Double.Parse(qualityFactory, CultureInfo.InvariantCulture)
: 1D;
}
}
/**
* Create a new {@link MediaType} for the given primary type.
* <p>The {@linkplain #getSubtype() subtype} is set to <code>&#42;</code>, parameters empty.
* @param type the primary type
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(string type) :
this(type, WILDCARD_TYPE)
{
}
/**
* Create a new {@link MediaType} for the given primary type and subtype.
* <p>The parameters are empty.
* @param type the primary type
* @param subtype the subtype
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(string type, string subtype) :
this(type, subtype, new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase))
{
}
/**
* Create a new {@link MediaType} for the given type, subtype, and character set.
* @param type the primary type
* @param subtype the subtype
* @param charSet the character set
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(string type, string subtype, string charSet) :
this(type, subtype)
{
this.parameters.Add(PARAM_CHARSET, charSet);
}
/**
* Create a new {@link MediaType} for the given type, subtype, and quality value.
*
* @param type the primary type
* @param subtype the subtype
* @param qualityValue the quality value
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(String type, String subtype, double qualityValue) :
this(type, subtype)
{
this.parameters.Add(PARAM_QUALITY_FACTOR, qualityValue.ToString(CultureInfo.InvariantCulture));
}
/**
* Copy-constructor that copies the type and subtype of the given {@link MediaType},
* and allows for different parameter.
* @param other the other media type
* @param parameters the parameters, may be <code>null</code>
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(MediaType other, IDictionary<string, string> parameters) :
this(other.Type, other.Subtype, parameters)
{
}
/**
* Create a new {@link MediaType} for the given type, subtype, and parameters.
* @param type the primary type
* @param subtype the subtype
* @param parameters the parameters, may be <code>null</code>
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(string type, string subtype, IDictionary<string, string> parameters)
{
AssertUtils.ArgumentHasText(type, "'type' must not be empty");
AssertUtils.ArgumentHasText(subtype, "'subtype' must not be empty");
//checkToken(type);
//checkToken(subtype);
this.type = type.ToLowerInvariant();
this.subtype = subtype.ToLowerInvariant();
this.parameters = new Dictionary<string, string>(parameters, StringComparer.InvariantCultureIgnoreCase);
//if (parameters.Count > 0)
//{
// NameValueCollection m = new NameValueCollection(parameters.Count, null, new CaseInsensitiveComparer());
// for (Map.Entry<String, String> entry : parameters.entrySet()) {
// String attribute = entry.getKey();
// String value = entry.getValue();
// checkParameters(attribute, value);
// m.put(attribute, unquote(value));
// }
// this.parameters = Collections.unmodifiableMap(m);
//}
//else
//{
// this.parameters = Collections.emptyMap();
//}
}
public override bool Equals(object obj)
{
if (this == obj)
{
return true;
}
if (obj is MediaType)
{
MediaType otherMediaType = (MediaType)obj;
if (this.type == otherMediaType.type &&
this.subtype == otherMediaType.subtype)
{
if (otherMediaType.parameters.Count == this.parameters.Count)
{
foreach(string key in this.parameters.Keys)
{
if (!otherMediaType.parameters.ContainsKey(key) ||
!String.Equals(otherMediaType.parameters[key], this.parameters[key]))
{
return false;
}
}
return true;
}
}
}
return false;
}
public override int GetHashCode()
{
int result = this.type.GetHashCode();
result = 31 * result + this.subtype.GetHashCode();
result = 31 * result + this.parameters.GetHashCode();
return result;
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.Append(this.type);
builder.Append('/');
builder.Append(this.subtype);
foreach(string key in this.parameters.Keys)
{
builder.Append(';');
builder.Append(key);
builder.Append('=');
builder.Append(this.parameters[key]);
}
return builder.ToString();
}
/**
* Checks the given token string for illegal characters, as defined in RFC 2616, section 2.2.
* @throws IllegalArgumentException in case of illegal characters
* @see <a href="http://tools.ietf.org/html/rfc2616#section-2.2">HTTP 1.1, section 2.2</a>
*/
//private void checkToken(String s) {
// for (int i=0; i < s.length(); i++ ) {
// char ch = s.charAt(i);
// if (!TOKEN.get(ch)) {
// throw new IllegalArgumentException("Invalid token character '" + ch + "' in token \"" + s + "\"");
// }
// }
//}
//private void checkParameters(String attribute, String value) {
// Assert.hasLength(attribute, "parameter attribute must not be empty");
// Assert.hasLength(value, "parameter value must not be empty");
// checkToken(attribute);
// if (PARAM_QUALITY_FACTOR.equals(attribute)) {
// value = unquote(value);
// double d = Double.parseDouble(value);
// Assert.isTrue(d >= 0D && d <= 1D,
// "Invalid quality value \"" + value + "\": should be between 0.0 and 1.0");
// }
// else if (PARAM_CHARSET.equals(attribute)) {
// value = unquote(value);
// Charset.forName(value);
// }
// else if (!isQuotedString(value)) {
// checkToken(value);
// }
//}
//private boolean isQuotedString(String s) {
// return s.length() > 1 && s.startsWith("\"") && s.endsWith("\"") ;
//}
//private String unquote(String s) {
// if (s == null) {
// return null;
// }
// return isQuotedString(s) ? s.substring(1, s.length() - 1) : s;
//}
/**
* Return a generic parameter value, given a parameter name.
* @param name the parameter name
* @return the parameter value; or <code>null</code> if not present
*/
public string GetParameter(string name)
{
return this.parameters[name];
}
/**
* Indicate whether this {@link MediaType} includes the given media type.
* <p>For instance, {@code text/*} includes {@code text/plain}, {@code text/html}, and {@code application/*+xml}
* includes {@code application/soap+xml}, etc. This method is non-symmetic.
* @param other the reference media type with which to compare
* @return <code>true</code> if this media type includes the given media type; <code>false</code> otherwise
*/
public bool Includes(MediaType other)
{
if (other == null)
{
return false;
}
if (this.IsWildcardType)
{
// */* includes anything
return true;
}
else if (this.type == other.type)
{
if (this.subtype == other.subtype || this.IsWildcardSubtype)
{
return true;
}
// application/*+xml includes application/soap+xml
int thisPlusIdx = this.subtype.IndexOf('+');
int otherPlusIdx = other.subtype.IndexOf('+');
if (thisPlusIdx != -1 && otherPlusIdx != -1)
{
string thisSubtypeNoSuffix = this.subtype.Substring(0, thisPlusIdx);
string thisSubtypeSuffix = this.subtype.Substring(thisPlusIdx + 1);
string otherSubtypeSuffix = other.subtype.Substring(otherPlusIdx + 1);
if (thisSubtypeSuffix == otherSubtypeSuffix && WILDCARD_TYPE == thisSubtypeNoSuffix)
{
return true;
}
}
}
return false;
}
/**
* Indicate whether this {@link MediaType} is compatible with the given media type.
* <p>For instance, {@code text/*} is compatible with {@code text/plain}, {@code text/html}, and vice versa.
* In effect, this method is similar to {@link #includes(MediaType)}, except that it's symmetric.
* @param other the reference media type with which to compare
* @return <code>true</code> if this media type is compatible with the given media type; <code>false</code> otherwise
*/
public bool IsCompatibleWith(MediaType other)
{
if (other == null)
{
return false;
}
if (this.IsWildcardType || other.IsWildcardType)
{
return true;
}
else if (this.type == other.type)
{
if (this.subtype == other.subtype || this.IsWildcardSubtype || other.IsWildcardSubtype)
{
return true;
}
// application/*+xml is compatible with application/soap+xml, and vice-versa
int thisPlusIdx = this.subtype.IndexOf('+');
int otherPlusIdx = other.subtype.IndexOf('+');
if (thisPlusIdx != -1 && otherPlusIdx != -1)
{
string thisSubtypeNoSuffix = this.subtype.Substring(0, thisPlusIdx);
string otherSubtypeNoSuffix = other.subtype.Substring(0, otherPlusIdx);
string thisSubtypeSuffix = this.subtype.Substring(thisPlusIdx + 1);
string otherSubtypeSuffix = other.subtype.Substring(otherPlusIdx + 1);
if (thisSubtypeSuffix == otherSubtypeSuffix &&
(WILDCARD_TYPE == thisSubtypeNoSuffix || WILDCARD_TYPE == otherSubtypeNoSuffix))
{
return true;
}
}
}
return false;
}
#region IComparable<MediaType> Membres
/**
* Compares this {@link MediaType} to another alphabetically.
* @param other media type to compare to
* @see #sortBySpecificity(List)
*/
public int CompareTo(MediaType other)
{
int comp = this.type.CompareTo(other.type);
if (comp != 0)
{
return comp;
}
comp = this.subtype.CompareTo(other.subtype);
if (comp != 0)
{
return comp;
}
comp = this.parameters.Count - other.parameters.Count;
if (comp != 0)
{
return comp;
}
foreach(string key in this.parameters.Keys)
{
if (!other.parameters.ContainsKey(key))
{
return -1;
}
comp = String.Compare(this.parameters[key], other.parameters[key]);
if (comp != 0)
{
return comp;
}
}
return 0;
}
#endregion
/**
* Parse the given String into a single {@link MediaType}.
* @param mediaType the string to parse
* @return the media type
* @throws IllegalArgumentException if the string cannot be parsed
*/
public static MediaType ParseMediaType(string mediaType)
{
AssertUtils.ArgumentHasText(mediaType, "'mediaType' must not be empty");
string[] parts = mediaType.Split(';');
string fullType = parts[0].Trim();
if (fullType == WILDCARD_TYPE)
{
fullType = "*/*";
}
int subIndex = fullType.IndexOf('/');
if (subIndex == -1)
{
throw new ArgumentException(
String.Format("'{0}' does not contain '/'", mediaType),
"mediaType");
}
if (subIndex == fullType.Length - 1)
{
throw new ArgumentException(
String.Format("'{0}' does not contain subtype after '/'", mediaType),
"mediaType");
}
string type = fullType.Substring(0, subIndex);
string subtype = fullType.Substring(subIndex + 1);
IDictionary<string, string> parameters = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
if (parts.Length > 1)
{
for (int i = 1; i < parts.Length; i++)
{
string parameter = parts[i].Trim();
int eqIndex = parameter.IndexOf('=');
if (eqIndex != -1)
{
string attribute = parameter.Substring(0, eqIndex);
string value = parameter.Substring(eqIndex + 1);
parameters.Add(attribute, value);
}
}
}
return new MediaType(type, subtype, parameters);
}
/**
* Parse the given, comma-seperated string into a list of {@link MediaType} objects.
* <p>This method can be used to parse an Accept or Content-Type header.
* @param mediaTypes the string to parse
* @return the list of media types
* @throws IllegalArgumentException if the string cannot be parsed
*/
public static List<MediaType> ParseMediaTypes(string mediaTypes)
{
List<MediaType> mediaTypeList = new List<MediaType>();
if (!StringUtils.HasLength(mediaTypes))
{
return mediaTypeList;
}
string[] tokens = mediaTypes.Split(',');
foreach (string token in tokens)
{
mediaTypeList.Add(ParseMediaType(token));
}
return mediaTypeList;
}
/**
* Return a string representation of the given list of {@link MediaType} objects.
* <p>This method can be used to for an {@code Accept} or {@code Content-Type} header.
* @param mediaTypes the string to parse
* @return the list of media types
* @throws IllegalArgumentException if the String cannot be parsed
*/
public static string ToString(IEnumerable<MediaType> mediaTypes)
{
StringBuilder builder = new StringBuilder();
foreach(MediaType mediaType in mediaTypes)
{
if (builder.Length > 0)
{
builder.Append(", ");
}
builder.Append(mediaType);
}
return builder.ToString();
}
/**
* Sorts the given list of {@link MediaType} objects by specificity.
* <p>Given two media types:
* <ol>
* <li>if either media type has a {@linkplain #isWildcardType() wildcard type}, then the media type without the
* wildcard is ordered before the other.</li>
* <li>if the two media types have different {@linkplain #getType() types}, then they are considered equal and
* remain their current order.</li>
* <li>if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype}, then the media type without
* the wildcard is sorted before the other.</li>
* <li>if the two media types have different {@linkplain #getSubtype() subtypes}, then they are considered equal
* and remain their current order.</li>
* <li>if the two media types have different {@linkplain #getQualityValue() quality value}, then the media type
* with the highest quality value is ordered before the other.</li>
* <li>if the two media types have a different amount of {@linkplain #getParameter(String) parameters}, then the
* media type with the most parameters is ordered before the other.</li>
* </ol>
* <p>For example:
* <blockquote>audio/basic &lt; audio/* &lt; *&#047;*</blockquote>
* <blockquote>audio/* &lt; audio/*;q=0.7; audio/*;q=0.3</blockquote>
* <blockquote>audio/basic;level=1 &lt; audio/basic</blockquote>
* <blockquote>audio/basic == text/html</blockquote>
* <blockquote>audio/basic == audio/wave</blockquote>
* @param mediaTypes the list of media types to be sorted
* @see <a href="http://tools.ietf.org/html/rfc2616#section-14.1">HTTP 1.1, section 14.1</a>
*/
public static void SortBySpecificity(List<MediaType> mediaTypes)
{
AssertUtils.ArgumentNotNull(mediaTypes, "mediaTypes");
if (mediaTypes.Count > 1)
{
mediaTypes.Sort(SPECIFICITY_COMPARER);
}
}
/**
* Sorts the given list of {@link MediaType} objects by quality value.
* <p>Given two media types:
* <ol>
* <li>if the two media types have different {@linkplain #getQualityValue() quality value}, then the media type
* with the highest quality value is ordered before the other.</li>
* <li>if either media type has a {@linkplain #isWildcardType() wildcard type}, then the media type without the
* wildcard is ordered before the other.</li>
* <li>if the two media types have different {@linkplain #getType() types}, then they are considered equal and
* remain their current order.</li>
* <li>if either media type has a {@linkplain #isWildcardSubtype() wildcard subtype}, then the media type without
* the wildcard is sorted before the other.</li>
* <li>if the two media types have different {@linkplain #getSubtype() subtypes}, then they are considered equal
* and remain their current order.</li>
* <li>if the two media types have a different amount of {@linkplain #getParameter(String) parameters}, then the
* media type with the most parameters is ordered before the other.</li>
* </ol>
* @param mediaTypes the list of media types to be sorted
* @see #getQualityValue()
*/
public static void SortByQualityValue(List<MediaType> mediaTypes)
{
AssertUtils.ArgumentNotNull(mediaTypes, "mediaTypes");
if (mediaTypes.Count > 1)
{
mediaTypes.Sort(QUALITY_VALUE_COMPARER);
}
}
public static IComparer<MediaType> SPECIFICITY_COMPARER = new SpecificityComparer();
public static IComparer<MediaType> QUALITY_VALUE_COMPARER = new QualityValueComparer();
#region SpecificityComparer
private class SpecificityComparer : IComparer<MediaType>
{
public int Compare(MediaType x, MediaType y)
{
if (x.IsWildcardType && !y.IsWildcardType)
{ // */* < audio/*
return 1;
}
else if (y.IsWildcardType && !x.IsWildcardType)
{ // audio/* > */*
return -1;
}
else if (x.type != y.type)
{ // audio/basic == text/html
return 0;
}
else
{ // mediaType1.type == mediaType2.type
if (x.IsWildcardSubtype && !y.IsWildcardSubtype)
{ // audio/* < audio/basic
return 1;
}
else if (y.IsWildcardSubtype && !x.IsWildcardSubtype)
{ // audio/basic > audio/*
return -1;
}
else if (x.subtype != y.subtype)
{ // audio/basic == audio/wave
return 0;
}
else
{ // mediaType2.subtype == mediaType2.subtype
double quality1 = x.QualityValue;
double quality2 = y.QualityValue;
int qualityComparison = quality2.CompareTo(quality1);
if (qualityComparison != 0)
{
return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3
}
else
{
int paramsSize1 = x.parameters.Count;
int paramsSize2 = y.parameters.Count;
return (paramsSize2 < paramsSize1 ? -1 : (paramsSize2 == paramsSize1 ? 0 : 1)); // audio/basic;level=1 < audio/basic
}
}
}
}
}
#endregion
#region QualityValueComparer
private class QualityValueComparer : IComparer<MediaType>
{
public int Compare(MediaType x, MediaType y)
{
double quality1 = x.QualityValue;
double quality2 = y.QualityValue;
int qualityComparison = quality2.CompareTo(quality1);
if (qualityComparison != 0)
{
return qualityComparison; // audio/*;q=0.7 < audio/*;q=0.3
}
else if (x.IsWildcardType && !y.IsWildcardType)
{ // */* < audio/*
return 1;
}
else if (y.IsWildcardType && !x.IsWildcardType)
{ // audio/* > */*
return -1;
}
else if (x.type != y.type)
{ // audio/basic == text/html
return 0;
}
else
{ // mediaType1.type == mediaType2.type
if (x.IsWildcardSubtype && !y.IsWildcardSubtype)
{ // audio/* < audio/basic
return 1;
}
else if (y.IsWildcardSubtype && !x.IsWildcardSubtype)
{ // audio/basic > audio/*
return -1;
}
else if (x.subtype != y.subtype)
{ // audio/basic == audio/wave
return 0;
}
else
{
int paramsSize1 = x.parameters.Count;
int paramsSize2 = y.parameters.Count;
return (paramsSize2 < paramsSize1 ? -1 : (paramsSize2 == paramsSize1 ? 0 : 1)); // audio/basic;level=1 < audio/basic
}
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,47 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
namespace Spring.Http.Rest
{
/**
* Callback interface for code that operates on a {@link ClientHttpRequest}. Allows to manipulate the request
* headers, and write to the request body.
*
* <p>Used internally by the {@link RestTemplate}, but also useful for application code.
*
* @author Arjen Poutsma
* @see RestTemplate#execute
* @since 3.0
*/
public interface IRequestCallback
{
/**
* Gets called by {@link RestTemplate#execute} with an opened {@code ClientHttpRequest}.
* Does not need to care about closing the request or about handling errors:
* this will all be handled by the {@code RestTemplate}.
* @param request the active HTTP request
* @throws IOException in case of I/O errors
*/
void DoWithRequest(HttpWebRequest request);
}
}

View File

@@ -0,0 +1,48 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
namespace Spring.Http.Rest
{
/**
* Generic callback interface used by {@link RestTemplate}'s retrieval methods
* Implementations of this interface perform the actual work of extracting data
* from a {@link ClientHttpResponse}, but don't need to worry about exception
* handling or closing resources.
*
* <p>Used internally by the {@link RestTemplate}, but also useful for application code.
*
* @author Arjen Poutsma
* @since 3.0
* @see RestTemplate#execute
*/
public interface IResponseExtractor<T> where T : class
{
/**
* Extract data from the given {@code ClientHttpResponse} and return it.
* @param response the HTTP response
* @return the extracted data
* @throws IOException in case of I/O errors
*/
T ExtractData(HttpWebResponse response); // throws IOException;
}
}

View File

@@ -0,0 +1,439 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
using Spring.Http;
namespace Spring.Http.Rest
{
public interface IRestOperations
{
#region GET
/**
* Retrieve a representation by doing a GET on the specified URL.
* The response (if any) is converted and returned.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @return the converted object
*/
T GetForObject<T>(string url, params string[] uriVariables) where T : class; //throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URI template.
* The response (if any) is converted and returned.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param responseType the type of the return value
* @param uriVariables the map containing variables for the URI template
* @return the converted object
*/
T GetForObject<T>(string url, IDictionary<string, string> uriVariables) where T : class; //throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URL .
* The response (if any) is converted and returned.
* @param url the URL
* @param responseType the type of the return value
* @return the converted object
*/
T GetForObject<T>(Uri url) where T : class; //throws RestClientException;
/**
* Retrieve an entity by doing a GET on the specified URL.
* The response is converted and stored in an {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @return the entity
* @since 3.0.2
*/
HttpResponseMessage<T> GetForMessage<T>(string url, params string[] uriVariables) where T : class; //throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URI template.
* The response is converted and stored in an {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param responseType the type of the return value
* @param uriVariables the map containing variables for the URI template
* @return the converted object
* @since 3.0.2
*/
HttpResponseMessage<T> GetForMessage<T>(string url, IDictionary<string, string> uriVariables) where T : class; //throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URL .
* The response is converted and stored in an {@link ResponseEntity}.
* @param url the URL
* @param responseType the type of the return value
* @return the converted object
* @since 3.0.2
*/
HttpResponseMessage<T> GetForMessage<T>(Uri url) where T : class; //throws RestClientException;
#endregion
#region HEAD
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param uriVariables the variables to expand the template
* @return all HTTP headers of that resource
*/
WebHeaderCollection HeadForHeaders(string url, params string[] uriVariables); //throws RestClientException;
/**
* Retrieve all headers of the resource specified by the URI template.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param uriVariables the map containing variables for the URI template
* @return all HTTP headers of that resource
*/
WebHeaderCollection HeadForHeaders(string url, IDictionary<string, string> uriVariables); //throws RestClientException;
/**
* Retrieve all headers of the resource specified by the URL.
* @param url the URL
* @return all HTTP headers of that resource
*/
WebHeaderCollection HeadForHeaders(Uri url); //throws RestClientException;
#endregion
#region POST
/**
* Create a new resource by POSTing the given object to the URI template, and returns the value of the
* <code>Location</code> header. This header typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @param uriVariables the variables to expand the template
* @return the value for the <code>Location</code> header
* @see HttpEntity
*/
Uri PostForLocation(string url, object request, params string[] uriVariables); //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template, and returns the value of the
* <code>Location</code> header. This header typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given map.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @param uriVariables the variables to expand the template
* @return the value for the <code>Location</code> header
* @see HttpEntity
*/
Uri PostForLocation(string url, object request, IDictionary<string, string> uriVariables); //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL, and returns the value of the
* <code>Location</code> header. This header typically indicates where the new resource is stored.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @return the value for the <code>Location</code> header
* @see HttpEntity
*/
Uri PostForLocation(Uri url, object request); //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
* and returns the representation found in the response.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
*/
T PostForObject<T>(string url, object request, params string[] uriVariables) where T : class; //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
* and returns the representation found in the response.
* <p>URI Template variables are expanded using the given map.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @param responseType the type of the return value
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
*/
T PostForObject<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class; //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL,
* and returns the representation found in the response.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @param responseType the type of the return value
* @return the converted object
* @see HttpEntity
*/
T PostForObject<T>(Uri url, object request) where T : class; //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
* and returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
* @since 3.0.2
*/
HttpResponseMessage<T> PostForMessage<T>(string url, object request, params string[] uriVariables) where T : class; //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template,
* and returns the response as {@link HttpEntity}.
* <p>URI Template variables are expanded using the given map.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @param uriVariables the variables to expand the template
* @return the converted object
* @see HttpEntity
* @since 3.0.2
*/
HttpResponseMessage<T> PostForMessage<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class; //throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL,
* and returns the response as {@link ResponseEntity}.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be POSTed, may be <code>null</code>
* @return the converted object
* @see HttpEntity
* @since 3.0.2
*/
HttpResponseMessage<T> PostForMessage<T>(Uri url, object request) where T : class; //throws RestClientException;
#endregion
#region PUT
/**
* Create or update a resource by PUTting the given object to the URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be <code>null</code>
* @param uriVariables the variables to expand the template
* @see HttpEntity
*/
void Put(string url, object request, params string[] uriVariables); //throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URI template.
* <p>URI Template variables are expanded using the given map.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be <code>null</code>
* @param uriVariables the variables to expand the template
* @see HttpEntity
*/
void Put(string url, object request, IDictionary<string, string> uriVariables); //throws RestClientException;
/**
* Creates a new resource by PUTting the given object to URL.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* @param url the URL
* @param request the Object to be PUT, may be <code>null</code>
* @see HttpEntity
*/
void Put(Uri url, object request); //throws RestClientException;
#endregion
#region DELETE
/**
* Delete the resources at the specified URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param uriVariables the variables to expand in the template
*/
void Delete(string url, params string[] uriVariables); //throws RestClientException;
/**
* Delete the resources at the specified URI.
* <p>URI Template variables are expanded using the given map.
*
* @param url the URL
* @param uriVariables the variables to expand the template
*/
void Delete(string url, IDictionary<string, string> uriVariables); //throws RestClientException;
/**
* Delete the resources at the specified URL.
* @param url the URL
*/
void Delete(Uri url); //throws RestClientException;
#endregion
#region OPTIONS
/**
* Return the value of the Allow header for the given URI.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param uriVariables the variables to expand in the template
* @return the value of the allow header
*/
IList<HttpMethod> OptionsForAllow(string url, params string[] uriVariables); //throws RestClientException;
/**
* Return the value of the Allow header for the given URI.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param uriVariables the variables to expand in the template
* @return the value of the allow header
*/
IList<HttpMethod> OptionsForAllow(string url, IDictionary<string, string> uriVariables); //throws RestClientException;
/**
* Return the value of the Allow header for the given URL.
* @param url the URL
* @return the value of the allow header
*/
IList<HttpMethod> OptionsForAllow(Uri url); //throws RestClientException;
#endregion
#region Exchange
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and
* returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null}
* @param responseType the type of the return value
* @param uriVariables the variables to expand in the template
* @return the response as entity
* @since 3.0.2
*/
HttpResponseMessage<T> Exchange<T>(string url, HttpRequestMessage requestMessage, params string[] uriVariables) where T : class; //throws RestClientException;
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and
* returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null}
* @param responseType the type of the return value
* @param uriVariables the variables to expand in the template
* @return the response as entity
* @since 3.0.2
*/
HttpResponseMessage<T> Exchange<T>(string url, HttpRequestMessage requestMessage, IDictionary<string, string> uriVariables) where T : class; //throws RestClientException;
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and
* returns the response as {@link ResponseEntity}.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null}
* @param responseType the type of the return value
* @return the response as entity
* @since 3.0.2
*/
HttpResponseMessage<T> Exchange<T>(Uri url, HttpRequestMessage requestMessage) where T : class; //throws RestClientException;
#endregion
#region General execution
/**
* Execute the HTTP method to the given URI template, preparing the request with the
* {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
T Execute<T>(string url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor, params string[] uriVariables) where T : class; //throws RestClientException;
/**
* Execute the HTTP method to the given URI template, preparing the request with the
* {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
* <p>URI Template variables are expanded using the given URI variables map.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @param uriVariables the variables to expand in the template
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
T Execute<T>(string url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor, IDictionary<string, string> uriVariables) where T : class; //throws RestClientException;
/**
* Execute the HTTP method to the given URL, preparing the request with the
* {@link RequestCallback}, and reading the response with a {@link ResponseExtractor}.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestCallback object that prepares the request
* @param responseExtractor object that extracts the return value from the response
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
T Execute<T>(Uri url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor) where T : class; //throws RestClientException;
#endregion
}
}

View File

@@ -0,0 +1,78 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Runtime.Serialization;
namespace Spring.Http.Rest
{
[Serializable]
public class RestClientException : Exception
{
/// <summary>
/// Creates a new instance of the
/// RestClientException class.
/// </summary>
public RestClientException()
{
}
/// <summary>
/// Creates a new instance of the RestClientException class.
/// </summary>
/// <param name="message">
/// A message about the exception.
/// </param>
public RestClientException(string message)
: base(message)
{
}
/// <summary>
/// Creates a new instance of the RestClientException class.
/// </summary>
/// <param name="message">
/// A message about the exception.
/// </param>
/// <param name="rootCause">
/// The root exception that is being wrapped.
/// </param>
public RestClientException(string message, Exception rootCause)
: base(message, rootCause)
{
}
/// <summary>
/// Creates a new instance of the RestClientException class.
/// </summary>
/// <param name="info">
/// The <see cref="System.Runtime.Serialization.SerializationInfo"/>
/// that holds the serialized object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The <see cref="System.Runtime.Serialization.StreamingContext"/>
/// that contains contextual information about the source or destination.
/// </param>
protected RestClientException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@@ -0,0 +1,553 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
using Spring.Util;
using Spring.Http;
using Spring.Http.Converters;
using Spring.Http.Converters.Xml;
using Spring.Http.Converters.Json;
using Spring.Http.Converters.Feed;
using Spring.Http.Rest.Support;
using UriTemplate = Spring.Util.UriTemplate; // UriTemplate in .NET Framework since 3.5
namespace Spring.Http.Rest
{
/**
* <strong>The central class for client-side HTTP access.</strong> It simplifies communication with HTTP servers, and
* enforces RESTful principles. It handles HTTP connections, leaving application code to provide URLs (with possible
* template variables) and extract results.
*
* <p>The main entry points of this template are the methods named after the six main HTTP methods:
* <table>
* <tr><th>HTTP method</th><th>RestTemplate methods</th></tr>
* <tr><td>DELETE</td><td>{@link #delete}</td></tr>
* <tr><td>GET</td><td>{@link #getForObject}</td></tr>
* <tr><td></td><td>{@link #getForEntity}</td></tr>
* <tr><td>HEAD</td><td>{@link #headForHeaders}</td></tr>
* <tr><td>OPTIONS</td><td>{@link #optionsForAllow}</td></tr>
* <tr><td>POST</td><td>{@link #postForLocation}</td></tr>
* <tr><td></td><td>{@link #postForObject}</td></tr>
* <tr><td>PUT</td><td>{@link #put}</td></tr>
* <tr><td>any</td><td>{@link #exchange}</td></tr>
* <tr><td></td><td>{@link #execute}</td></tr> </table>
*
* <p>For each of these HTTP methods, there are three corresponding Java methods in the {@code RestTemplate}. Two
* variant take a {@code String} URI as first argument (eg. {@link #getForObject(String, Class, Object[])}, {@link
* #getForObject(String, Class, Map)}), and are capable of substituting any {@linkplain UriTemplate URI templates} in
* that URL using either a {@code String} variable arguments array, or a {@code Map<String, String>}. The string varargs
* variant expands the given template variables in order, so that
* <pre>
* String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42",
* "21");
* </pre>
* will perform a GET on {@code http://example.com/hotels/42/bookings/21}. The map variant expands the template based on
* variable name, and is therefore more useful when using many variables, or when a single variable is used multiple
* times. For example:
* <pre>
* Map&lt;String, String&gt; vars = Collections.singletonMap("hotel", "42");
* String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
* </pre>
* will perform a GET on {@code http://example.com/hotels/42/rooms/42}. Alternatively, there are {@link URI} variant
* methods ({@link #getForObject(URI, Class)}), which do not allow for URI templates, but allow you to reuse a single,
* expanded URI multiple times.
*
* <p>Furthermore, the {@code String}-argument methods assume that the URL String is unencoded. This means that
* <pre>
* restTemplate.getForObject("http://example.com/hotel list");
* </pre>
* will perform a GET on {@code http://example.com/hotel%20list}. As a result, any URL passed that is already encoded
* will be encoded twice (i.e. {@code http://example.com/hotel%20list} will become {@code
* http://example.com/hotel%2520list}). If this behavior is undesirable, use the {@code URI}-argument methods, which
* will not perform any URL encoding.
*
* <p>Objects passed to and returned from these methods are converted to and from HTTP messages by {@link
* HttpMessageConverter} instances. Converters for the main mime types are registered by default, but you can also write
* your own converter and register it via the {@link #setMessageConverters messageConverters} bean property.
*
* <p>This template uses a {@link org.springframework.http.client.SimpleClientHttpRequestFactory} and a {@link
* DefaultResponseErrorHandler} as default strategies for creating HTTP connections or handling HTTP errors,
* respectively. These defaults can be overridden through the {@link #setRequestFactory(ClientHttpRequestFactory)
* requestFactory} and {@link #setErrorHandler(ResponseErrorHandler) errorHandler} bean properties.
*
* @author Arjen Poutsma
* @see HttpMessageConverter
* @see RequestCallback
* @see ResponseExtractor
* @see ResponseErrorHandler
* @since 3.0
*/
public class RestTemplate : IRestOperations
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(RestTemplate));
#endregion
#region Fields / Properties
private Uri _baseAddress;
private bool _throwExceptionOnError;
private IList<IHttpMessageConverter> _messageConverters;
private IHttpWebRequestFactory _requestFactory;
private IResponseExtractor<WebHeaderCollection> headersExtractor;
public Uri BaseAddress
{
get
{
return this._baseAddress;
}
set
{
AssertUtils.ArgumentNotNull(value, "BaseAddress");
if (!value.IsAbsoluteUri)
{
throw new ArgumentException(String.Format("'{0}' is not an absolute URI", value), "BaseAddress");
}
this._baseAddress = value;
}
}
public bool ThrowExceptionOnError
{
get { return _throwExceptionOnError; }
set { _throwExceptionOnError = value; }
}
public IList<IHttpMessageConverter> MessageConverters
{
get { return this._messageConverters; }
set { this._messageConverters = value; }
}
public IHttpWebRequestFactory RequestFactory
{
get { return this._requestFactory; }
set { this._requestFactory = value; }
}
#endregion
#region Constructor(s)
public RestTemplate(Uri baseAddress) :
this()
{
this.BaseAddress = baseAddress;
}
public RestTemplate(string baseAddress) :
this()
{
this.BaseAddress = new Uri(baseAddress, UriKind.Absolute);
}
/** Create a new instance of the {@link RestTemplate} using default settings. */
public RestTemplate()
{
this._throwExceptionOnError = true;
this.headersExtractor = new HeadersResponseExtractor();
this._requestFactory = new DefaultHttpWebRequestFactory();
this._messageConverters = new List<IHttpMessageConverter>();
#if NET_3_5
//this._messageConverters.Add(new JsonHttpMessageConverter());
this._messageConverters.Add(new Atom10FeedHttpMessageConverter());
this._messageConverters.Add(new Rss20FeedHttpMessageConverter());
this._messageConverters.Add(new XElementHttpMessageConverter());
#endif
this._messageConverters.Add(new XmlDocumentHttpMessageConverter());
//this._messageConverters.Add(new XmlSerializableHttpMessageConverter());
this._messageConverters.Add(new StringHttpMessageConverter());
this._messageConverters.Add(new ByteArrayHttpMessageConverter());
}
#endregion
#region IRestOperations Membres
public T GetForObject<T>(string url, params string[] uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
MessageConverterResponseExtractor<T> responseExtractor = new MessageConverterResponseExtractor<T>(this._messageConverters);
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
public T GetForObject<T>(string url, IDictionary<string, string> uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
MessageConverterResponseExtractor<T> responseExtractor = new MessageConverterResponseExtractor<T>(this._messageConverters);
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
public T GetForObject<T>(Uri url) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
MessageConverterResponseExtractor<T> responseExtractor = new MessageConverterResponseExtractor<T>(this._messageConverters);
return this.Execute<T>(url, requestCallback, responseExtractor);
}
public HttpResponseMessage<T> GetForMessage<T>(string url, params string[] uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
public HttpResponseMessage<T> GetForMessage<T>(string url, IDictionary<string, string> uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
public HttpResponseMessage<T> GetForMessage<T>(Uri url) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor);
}
public WebHeaderCollection HeadForHeaders(string url, params string[] uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.HEAD);
return this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor, uriVariables);
}
public WebHeaderCollection HeadForHeaders(string url, IDictionary<string, string> uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.HEAD);
return this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor, uriVariables);
}
public WebHeaderCollection HeadForHeaders(Uri url)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.HEAD);
return this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor);
}
public Uri PostForLocation(string url, object request, params string[] uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, this._messageConverters);
WebHeaderCollection headers = this.Execute<WebHeaderCollection>(
url, requestCallback, this.headersExtractor, uriVariables);
string location = headers[HttpResponseHeader.Location];
return StringUtils.HasText(location) ? new Uri(location) : null;
}
public Uri PostForLocation(string url, object request, IDictionary<string, string> uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, this._messageConverters);
WebHeaderCollection headers = this.Execute<WebHeaderCollection>(
url, requestCallback, this.headersExtractor, uriVariables);
string location = headers[HttpResponseHeader.Location];
return StringUtils.HasText(location) ? new Uri(location) : null;
}
public Uri PostForLocation(Uri url, object request)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, this._messageConverters);
WebHeaderCollection headers = this.Execute<WebHeaderCollection>(
url, requestCallback, this.headersExtractor);
string location = headers[HttpResponseHeader.Location];
return StringUtils.HasText(location) ? new Uri(location) : null;
}
public T PostForObject<T>(string url, object request, params string[] uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, typeof(T), this._messageConverters);
MessageConverterResponseExtractor<T> responseExtractor = new MessageConverterResponseExtractor<T>(this._messageConverters);
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
public T PostForObject<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, typeof(T), this._messageConverters);
MessageConverterResponseExtractor<T> responseExtractor = new MessageConverterResponseExtractor<T>(this._messageConverters);
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
public T PostForObject<T>(Uri url, object request) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, typeof(T), this._messageConverters);
MessageConverterResponseExtractor<T> responseExtractor = new MessageConverterResponseExtractor<T>(this._messageConverters);
return this.Execute<T>(url, requestCallback, responseExtractor);
}
public HttpResponseMessage<T> PostForMessage<T>(string url, object request, params string[] uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
public HttpResponseMessage<T> PostForMessage<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
public HttpResponseMessage<T> PostForMessage<T>(Uri url, object request) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor);
}
public void Put(string url, object request, params string[] uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.PUT, request, this._messageConverters);
this.Execute<object>(url, requestCallback, null, uriVariables);
}
public void Put(string url, object request, IDictionary<string, string> uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.PUT, request, this._messageConverters);
this.Execute<object>(url, requestCallback, null, uriVariables);
}
public void Put(Uri url, object request)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.PUT, request, this._messageConverters);
this.Execute<object>(url, requestCallback, null);
}
public void Delete(string url, params string[] uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.DELETE);
this.Execute<object>(url, requestCallback, null, uriVariables);
}
public void Delete(string url, IDictionary<string, string> uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.DELETE);
this.Execute<object>(url, requestCallback, null, uriVariables);
}
public void Delete(Uri url)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.DELETE);
this.Execute<object>(url, requestCallback, null);
}
public IList<HttpMethod> OptionsForAllow(string url, params string[] uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.OPTIONS);
WebHeaderCollection headers = this.Execute<WebHeaderCollection>(
url, requestCallback, this.headersExtractor, uriVariables);
string allow = headers[HttpResponseHeader.Allow];
return ParseAllowHeader(allow);
}
public IList<HttpMethod> OptionsForAllow(string url, IDictionary<string, string> uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.OPTIONS);
WebHeaderCollection headers = this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor, uriVariables);
string allow = headers[HttpResponseHeader.Allow];
return ParseAllowHeader(allow);
}
public IList<HttpMethod> OptionsForAllow(Uri url)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.OPTIONS);
WebHeaderCollection headers = this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor);
string allow = headers[HttpResponseHeader.Allow];
return ParseAllowHeader(allow);
}
public HttpResponseMessage<T> Exchange<T>(string url, HttpRequestMessage requestMessage, params string[] uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
public HttpResponseMessage<T> Exchange<T>(string url, HttpRequestMessage requestMessage, IDictionary<string, string> uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
public HttpResponseMessage<T> Exchange<T>(Uri url, HttpRequestMessage requestMessage) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, typeof(T), this._messageConverters);
HttpMessageResponseExtractor<T> responseExtractor = new HttpMessageResponseExtractor<T>(this._messageConverters);
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor);
}
public T Execute<T>(string url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor, params string[] uriVariables) where T : class
{
UriTemplate uriTemplate = new UriTemplate(url);
Uri uri = uriTemplate.Expand(uriVariables);
return this.DoExecute<T>(uri, requestCallback, responseExtractor);
}
public T Execute<T>(string url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor, IDictionary<string, string> uriVariables) where T : class
{
UriTemplate uriTemplate = new UriTemplate(url);
Uri uri = uriTemplate.Expand(uriVariables);
return this.DoExecute<T>(uri, requestCallback, responseExtractor);
}
public T Execute<T>(Uri url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor) where T : class
{
return this.DoExecute<T>(url, requestCallback, responseExtractor);
}
#endregion
/**
* Execute the given method on the provided URI. The {@link ClientHttpRequest} is processed using the {@link
* RequestCallback}; the response with the {@link ResponseExtractor}.
* @param url the fully-expanded URL to connect to
* @param method the HTTP method to execute (GET, POST, etc.)
* @param requestCallback object that prepares the request (can be <code>null</code>)
* @param responseExtractor object that extracts the return value from the response (can be <code>null</code>)
* @return an arbitrary object, as returned by the {@link ResponseExtractor}
*/
protected virtual T DoExecute<T>(Uri url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor) where T : class
{
HttpWebRequest request;
HttpWebResponse response = null;
Uri finalUri = url;
if (!url.IsAbsoluteUri)
{
if (this._baseAddress != null)
{
finalUri = new Uri(this._baseAddress, url);
}
else
{
throw new ArgumentException(String.Format("'{0}' is not an absolute URI", url), "url");
}
}
// Create and initialize the web request
request = this._requestFactory.CreateRequest(finalUri);
if (requestCallback != null)
{
requestCallback.DoWithRequest(request);
}
try
{
// Get response
response = request.GetResponse() as HttpWebResponse;
if (request.HaveResponse == true && response != null)
{
#region Instrumentation
if (LOG.IsDebugEnabled)
{
LOG.Debug(String.Format(
"Request for '{0}' resulted in {1:d} - {1} ({2})",
finalUri, response.StatusCode, response.StatusDescription));
}
#endregion
if (responseExtractor != null)
{
return responseExtractor.ExtractData(response);
}
}
}
catch (WebException ex)
{
// This exception will be raised if the server didn't return 200 - OK
// Try to retrieve more information about the network error
if (ex.Response != null)
{
using (HttpWebResponse errorResponse = (HttpWebResponse)ex.Response)
{
if (this._throwExceptionOnError)
{
throw new RestClientException(String.Format(
"The server returned '{0}' with the status code {1:d} - {1}.",
errorResponse.StatusDescription, errorResponse.StatusCode),
ex);
}
else
{
if (responseExtractor != null)
{
return responseExtractor.ExtractData(errorResponse);
}
}
}
}
}
finally
{
if (response != null)
{
response.Close();
}
}
return null;
}
private static IList<HttpMethod> ParseAllowHeader(string allow)
{
IList<HttpMethod> methods = new List<HttpMethod>();
if (StringUtils.HasText(allow))
{
string[] methodsArray = allow.Split(',');
foreach (string method in methodsArray)
{
methods.Add((HttpMethod)Enum.Parse(typeof(HttpMethod), method.Trim(), true));
}
}
return methods;
}
}
}

View File

@@ -0,0 +1,96 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
using Spring.Http;
using Spring.Http.Converters;
namespace Spring.Http.Rest.Support
{
/**
* Request callback implementation that prepares the request's accept headers.
*/
public class AcceptHeaderRequestCallback : MethodRequestCallback
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(AcceptHeaderRequestCallback));
#endregion
protected Type responseType;
protected IList<IHttpMessageConverter> messageConverters;
public AcceptHeaderRequestCallback(HttpMethod method, Type responseType, IList<IHttpMessageConverter> messageConverters) :
base(method)
{
this.responseType = responseType;
this.messageConverters = messageConverters;
}
public override void DoWithRequest(HttpWebRequest request)
{
base.DoWithRequest(request);
if (responseType != null)
{
List<MediaType> allSupportedMediaTypes = new List<MediaType>();
foreach (IHttpMessageConverter messageConverter in this.messageConverters)
{
if (messageConverter.CanRead(responseType, null))
{
foreach (MediaType supportedMediaType in messageConverter.SupportedMediaTypes)
{
if (!String.IsNullOrEmpty(supportedMediaType.CharSet))
{
allSupportedMediaTypes.Add(new MediaType(
supportedMediaType.Type, supportedMediaType.Subtype));
}
else
{
allSupportedMediaTypes.Add(supportedMediaType);
}
}
}
}
if (allSupportedMediaTypes.Count > 0)
{
MediaType.SortBySpecificity(allSupportedMediaTypes);
#region Instrumentation
if (LOG.IsDebugEnabled)
{
LOG.Debug(String.Format(
"Setting request Accept header to '{0}'",
MediaType.ToString(allSupportedMediaTypes)));
}
#endregion
request.Accept = MediaType.ToString(allSupportedMediaTypes);
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System.Net;
namespace Spring.Http.Rest.Support
{
/**
* Response extractor that extracts the response {@link HttpHeaders}.
*/
public class HeadersResponseExtractor : IResponseExtractor<WebHeaderCollection>
{
public WebHeaderCollection ExtractData(HttpWebResponse response)
{
return response.Headers;
}
}
}

View File

@@ -0,0 +1,132 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
using Spring.Util;
using Spring.Http;
using Spring.Http.Converters;
namespace Spring.Http.Rest.Support
{
/**
* Request callback implementation that writes the given object to the request stream.
*/
public class HttpMessageRequestCallback : AcceptHeaderRequestCallback
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(HttpMessageRequestCallback));
#endregion
private HttpRequestMessage requestMessage;
public HttpMessageRequestCallback(HttpMethod method, object requestBody, IList<IHttpMessageConverter> messageConverters) :
this(method, requestBody, null, messageConverters)
{
}
public HttpMessageRequestCallback(HttpMethod method, object requestBody, Type responseType, IList<IHttpMessageConverter> messageConverters) :
base(method, responseType, messageConverters)
{
if (requestBody is HttpRequestMessage)
{
this.requestMessage = (HttpRequestMessage)requestBody;
this.requestMessage.Method = method;
}
else
{
this.requestMessage = new HttpRequestMessage(requestBody, method);
}
}
public HttpMessageRequestCallback(HttpRequestMessage requestMessage, IList<IHttpMessageConverter> messageConverters) :
this(requestMessage, null, messageConverters)
{
}
public HttpMessageRequestCallback(HttpRequestMessage requestMessage, Type responseType, IList<IHttpMessageConverter> messageConverters) :
base(requestMessage.Method, responseType, messageConverters)
{
this.requestMessage = requestMessage;
}
public override void DoWithRequest(HttpWebRequest request)
{
base.DoWithRequest(request);
// headers
if (requestMessage.Headers.Count > 0)
{
request.Headers.Add(requestMessage.Headers);
}
// body
if (requestMessage.Body != null)
{
object requestBody = requestMessage.Body;
MediaType requestContentType = null;
if (StringUtils.HasText(requestMessage.Headers[HttpRequestHeader.ContentType]))
{
requestContentType = MediaType.ParseMediaType(requestMessage.Headers[HttpRequestHeader.ContentType]);
}
foreach (IHttpMessageConverter messageConverter in base.messageConverters)
{
if (messageConverter.CanWrite(requestBody.GetType(), requestContentType))
{
#region Instrumentation
if (LOG.IsDebugEnabled)
{
if (requestContentType != null)
{
LOG.Debug(String.Format(
"Writing [{0}] as '{1}' using [{2}]",
requestBody, requestContentType, messageConverter));
}
else
{
LOG.Debug(String.Format(
"Writing [{0}] using [{1}]",
requestBody, messageConverter));
}
}
#endregion
messageConverter.Write(requestBody, requestContentType, request);
return;
}
}
string message = String.Format(
"Could not write request: no suitable IHttpMessageConverter found for request type [{0}]",
requestBody.GetType().FullName);
if (requestContentType != null)
{
message = String.Format("{0} and content type [{1}]", message, requestContentType);
}
throw new RestClientException(message);
}
}
}
}

View File

@@ -0,0 +1,55 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System.Net;
using System.Collections.Generic;
using Spring.Util;
using Spring.Http;
using Spring.Http.Converters;
namespace Spring.Http.Rest.Support
{
/**
* Response extractor for {@link HttpEntity}.
*/
public class HttpMessageResponseExtractor<T> : IResponseExtractor<HttpResponseMessage<T>> where T : class
{
private MessageConverterResponseExtractor<T> httpMessageConverterExtractor;
public HttpMessageResponseExtractor(IList<IHttpMessageConverter> messageConverters)
{
httpMessageConverterExtractor = new MessageConverterResponseExtractor<T>(messageConverters);
}
public HttpResponseMessage<T> ExtractData(HttpWebResponse response)
{
if (StringUtils.HasText(response.Headers[HttpResponseHeader.ContentType]))
{
T body = httpMessageConverterExtractor.ExtractData(response);
return new HttpResponseMessage<T>(body, response.Headers, response.StatusCode, response.StatusDescription);
}
else
{
return new HttpResponseMessage<T>(response.Headers, response.StatusCode, response.StatusDescription);
}
}
}
}

View File

@@ -0,0 +1,88 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
using Spring.Util;
using Spring.Http;
using Spring.Http.Converters;
namespace Spring.Http.Rest.Support
{
/**
* Response extractor that uses the given {@linkplain HttpMessageConverter entity converters} to convert the response
* into a type <code>T</code>.
*
* @author Arjen Poutsma
* @see RestTemplate
* @since 3.0
*/
public class MessageConverterResponseExtractor<T> : IResponseExtractor<T> where T : class
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(MessageConverterResponseExtractor<T>));
#endregion
private IList<IHttpMessageConverter> messageConverters;
/**
* Creates a new instance of the {@code HttpMessageConverterExtractor} with the given response type and message
* converters. The given converters must support the response type.
*/
public MessageConverterResponseExtractor(IList<IHttpMessageConverter> messageConverters)
{
this.messageConverters = messageConverters;
}
public T ExtractData(HttpWebResponse response)
{
if (!StringUtils.HasText(response.Headers[HttpResponseHeader.ContentType]))
{
throw new RestClientException("Could not extract response: no Content-Type found");
}
MediaType contentType = MediaType.ParseMediaType(response.Headers[HttpResponseHeader.ContentType]);
foreach(IHttpMessageConverter messageConverter in messageConverters)
{
if (messageConverter.CanRead(typeof(T), contentType))
{
#region Instrumentation
if (LOG.IsDebugEnabled)
{
LOG.Debug(String.Format(
"Reading [{0}] as '{1}' using [{2}]",
typeof(T).FullName, contentType, messageConverter));
}
#endregion
return messageConverter.Read<T>(response);
}
}
throw new RestClientException(String.Format(
"Could not extract response: no suitable HttpMessageConverter found for response type [{0}] and content type [{1}]",
typeof(T).FullName, contentType));
}
}
}

View File

@@ -0,0 +1,60 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using Spring.Http;
namespace Spring.Http.Rest.Support
{
/**
* Request callback implementation that sets the Http method.
*/
public class MethodRequestCallback : IRequestCallback
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(MethodRequestCallback));
#endregion
protected HttpMethod method;
public MethodRequestCallback(HttpMethod method)
{
this.method = method;
}
public virtual void DoWithRequest(HttpWebRequest request)
{
#region Instrumentation
if (LOG.IsDebugEnabled)
{
LOG.Debug(String.Format("Setting request Method to '{0}'", this.method));
}
#endregion
request.Method = this.method.ToString();
}
}
}

View File

@@ -0,0 +1,146 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>Spring.Http</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>Spring</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<SignAssembly>false</SignAssembly>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<TargetFrameworkSubset>
</TargetFrameworkSubset>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2008\Spring.Http\Debug\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;DEBUG;NET_2_0;NET_3_0;NET_3_5</DefineConstants>
<DocumentationFile>Spring.Http.xml</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2008\Spring.Http\Release\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;NET_2_0;NET_3_0;NET_3_5</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=2.1.0.0, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
</Reference>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.Runtime.Serialization">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="System.ServiceModel.Web">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>CommonAssemblyInfo.cs</Link>
<SubType>Code</SubType>
</Compile>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Http\Converters\Feed\Atom10FeedHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Feed\AbstractFeedHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Feed\RssFeedHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Json\JsonHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\AbstractXmlHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\XElementHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\XmlDocumentHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\XmlSerializableHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\DataContractHttpMessageConverter.cs" />
<Compile Include="Http\DefaultHttpWebRequestFactory.cs" />
<Compile Include="Http\HttpResponseMessage.cs" />
<Compile Include="Http\IHttpWebRequestFactory.cs" />
<Compile Include="Http\HttpRequestMessage.cs" />
<Compile Include="Http\Rest\Support\AcceptHeaderRequestCallback.cs" />
<Compile Include="Http\Rest\Support\MethodRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HttpMessageRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HeadersResponseExtractor.cs" />
<Compile Include="Http\Rest\Support\HttpMessageResponseExtractor.cs" />
<Compile Include="Http\Converters\AbstractHttpMessageConverter.cs" />
<Compile Include="Http\Converters\ByteArrayHttpMessageConverter.cs" />
<Compile Include="Http\Rest\Support\MessageConverterResponseExtractor.cs" />
<Compile Include="Http\HttpMethod.cs" />
<Compile Include="Http\Converters\IHttpMessageConverter.cs" />
<Compile Include="Http\Rest\IRequestCallback.cs" />
<Compile Include="Http\Rest\IResponseExtractor.cs" />
<Compile Include="Http\Rest\IRestOperations.cs" />
<Compile Include="Http\MediaType.cs" />
<Compile Include="Http\Rest\RestClientException.cs" />
<Compile Include="Http\Rest\RestTemplate.cs" />
<Compile Include="Http\Converters\StringHttpMessageConverter.cs" />
<Compile Include="Util\UriTemplate.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spring.Core\Spring.Core.2008.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2008</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>Spring.Http</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>Spring</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<SignAssembly>false</SignAssembly>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation />
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2010\Spring.Http\Debug\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;DEBUG;NET_2_0;NET_3_0;NET_3_5</DefineConstants>
<DocumentationFile>Spring.Http.xml</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2010\Spring.Http\Release\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;NET_2_0;NET_3_0;NET_3_5</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=2.1.0.0, Culture=neutral, PublicKeyToken=65e474d141e25e07, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
</Reference>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.configuration" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel.Web" />
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>CommonAssemblyInfo.cs</Link>
<SubType>Code</SubType>
</Compile>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Http\HttpResponseMessage.cs" />
<Compile Include="Http\DefaultHttpWebRequestFactory.cs" />
<Compile Include="Http\Converters\AbstractHttpMessageConverter.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Http\Converters\ByteArrayHttpMessageConverter.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Http\Converters\Feed\AbstractFeedHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Feed\Atom10FeedHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Feed\RssFeedHttpMessageConverter.cs" />
<Compile Include="Http\Converters\IHttpMessageConverter.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Http\Converters\Json\JsonHttpMessageConverter.cs" />
<Compile Include="Http\Converters\StringHttpMessageConverter.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Http\Converters\Xml\AbstractXmlHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\DataContractHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\XElementHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\XmlDocumentHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\XmlSerializableHttpMessageConverter.cs" />
<Compile Include="Http\HttpMethod.cs" />
<Compile Include="Http\HttpRequestMessage.cs" />
<Compile Include="Http\IHttpWebRequestFactory.cs" />
<Compile Include="Http\MediaType.cs" />
<Compile Include="Http\Rest\IRequestCallback.cs" />
<Compile Include="Http\Rest\IResponseExtractor.cs" />
<Compile Include="Http\Rest\IRestOperations.cs" />
<Compile Include="Http\Rest\RestClientException.cs" />
<Compile Include="Http\Rest\RestTemplate.cs" />
<Compile Include="Http\Rest\Support\AcceptHeaderRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HeadersResponseExtractor.cs" />
<Compile Include="Http\Rest\Support\HttpMessageRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HttpMessageResponseExtractor.cs" />
<Compile Include="Http\Rest\Support\MessageConverterResponseExtractor.cs" />
<Compile Include="Http\Rest\Support\MethodRequestCallback.cs" />
<Compile Include="Util\UriTemplate.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spring.Core\Spring.Core.2010.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2010</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,239 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
namespace Spring.Util
{
// TODO : Check .NET 3.5 class
// TODO : Back to original Java behavior Expand(params string[]) method ?
/**
* Represents a URI template. An URI template is a URI-like String that contained variables marked of in braces
* (<code>{</code>, <code>}</code>), which can be expanded to produce a URI. <p>See {@link #expand(Map)},
* {@link #expand(Object[])}, and {@link #match(String)} for example usages.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
* @see <a href="http://bitworking.org/projects/URI-Templates/">URI Templates</a>
*/
public class UriTemplate
{
/** Captures URI template variable names. */
private static Regex VARIABLENAMES_REGEX = new Regex(@"\{([^/]+?)\}", RegexOptions.Compiled);
//private static Regex VARIABLENAMES_REGEX = new Regex(@"\{[^{}]+\}", RegexOptions.Compiled);
/** Replaces template variables in the URI template. */
private static string VARIABLEVALUE_PATTERN = "(?<{0}>.*)";
private const string BRACE_LEFT = "{";
private const string BRACE_RIGHT = "}";
private string uriTemplate;
private string[] variableNames;
private Regex matchRegex;
public string[] VariableNames
{
get { return this.variableNames; }
}
public UriTemplate(string uriTemplate)
{
this.uriTemplate = uriTemplate;
Parser parser = new Parser(uriTemplate);
this.variableNames = parser.GetVariableNames();
this.matchRegex = parser.GetMatchRegex();
}
public Uri Expand(IDictionary<string, string> uriVariables)
{
if (uriVariables.Count != this.variableNames.Length)
{
throw new ArgumentException(String.Format(
"Invalid amount of variables values in '{0}': expected {1}; got {2}",
this.uriTemplate, this.variableNames.Length, uriVariables.Count));
}
string uri = this.uriTemplate;
foreach (string variableName in this.variableNames)
{
if (!uriVariables.ContainsKey(variableName))
{
throw new ArgumentException(String.Format(
"'uriVariables' dictionary has no value for '{0}'",
variableName));
}
uri = Replace(uri, variableName, uriVariables[variableName]);
}
return new Uri(uri, UriKind.RelativeOrAbsolute);
//string[] uriVariableValues = new String[this.variableNames.Length];
//for (int i = 0; i < this.variableNames.Length; i++)
//{
// string variableName = this.variableNames[i];
// if (!uriVariables.ContainsKey(variableName))
// {
// throw new ArgumentException(String.Format(
// "'uriVariables' dictionary has no value for '{0}'",
// variableName));
// }
// uriVariableValues[i] = uriVariables[variableName];
//}
//return Expand(uriVariableValues);
}
public Uri Expand(params string[] uriVariableValues)
{
if (uriVariableValues.Length != this.variableNames.Length)
{
throw new ArgumentException(String.Format(
"Invalid amount of variables values in '{0}': expected {1}; got {2}",
this.uriTemplate, this.variableNames.Length, uriVariableValues.Length));
}
string uri = this.uriTemplate;
for (int i = 0; i < this.variableNames.Length; i++)
{
uri = Replace(uri, this.variableNames[i], uriVariableValues[i]);
}
return new Uri(uri, UriKind.RelativeOrAbsolute);
}
/**
* Indicate whether the given URI matches this template.
* @param uri the URI to match to
* @return <code>true</code> if it matches; <code>false</code> otherwise
*/
public bool Matches(string uri)
{
if (uri == null)
{
return false;
}
return this.matchRegex.IsMatch(uri);
}
/**
* Match the given URI to a map of variable values. Keys in the returned map are variable names, values are variable
* values, as occurred in the given URI. <p>Example: <pre class="code"> UriTemplate template = new
* UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}"); System.out.println(template.match("http://example.com/hotels/1/bookings/42"));
* </pre> will print: <blockquote><code>{hotel=1, booking=42}</code></blockquote>
* @param uri the URI to match to
* @return a map of variable values
*/
public IDictionary<string, string> Match(string uri)
{
AssertUtils.ArgumentNotNull(uri, "uri");
IDictionary<string, string> result = new Dictionary<string, string>();
Match match = this.matchRegex.Match(uri);
for (int i = 1; i < match.Groups.Count; i++ )
{
result.Add(this.matchRegex.GroupNameFromNumber(i), match.Groups[i].Value);
}
return result;
}
public override string ToString()
{
return this.uriTemplate;
}
//private static string[] GetVariableNames(string uriTemplate)
//{
// List<string> variableNames = new List<string>();
// foreach (Match match in VARIABLENAMES_REGEX.Matches(uriTemplate))
// {
// string token = match.Value;
// token = token.Substring(1, token.Length - 2);
// if (!variableNames.Contains(token))
// {
// variableNames.Add(token);
// }
// }
// return variableNames.ToArray();
//}
private static string Replace(string uriTemplate, string token, string value)
{
string quotedToken = BRACE_LEFT + token + BRACE_RIGHT;
return uriTemplate.Replace(quotedToken, value);
}
/**
* Static inner class to parse uri template strings into a matching regular expression.
*/
private class Parser
{
private List<String> variableNames = new List<String>();
private StringBuilder patternBuilder = new StringBuilder();
public Parser(string uriTemplate)
{
AssertUtils.ArgumentHasText(uriTemplate, "'uriTemplate' must not be null");
int index = 0;
this.patternBuilder.Append("^");
foreach (Match match in VARIABLENAMES_REGEX.Matches(uriTemplate))
{
string variableName = match.Groups[1].Value;
if (!variableNames.Contains(variableName))
{
variableNames.Add(variableName);
}
this.patternBuilder.Append(Escape(uriTemplate, index, match.Index - index));
this.patternBuilder.Append(String.Format(VARIABLEVALUE_PATTERN, variableName));
index = match.Index + match.Length;
}
this.patternBuilder.Append(Escape(uriTemplate, index, uriTemplate.Length - index));
this.patternBuilder.Append("$");
}
private static string Escape(String fullPath, int start, int end)
{
if (start == end)
{
return "";
}
return Regex.Escape(fullPath.Substring(start, end));
}
public string[] GetVariableNames()
{
return this.variableNames.ToArray();
}
public Regex GetMatchRegex()
{
return new Regex(this.patternBuilder.ToString(), RegexOptions.Compiled);
}
}
}
}

View File

@@ -0,0 +1,25 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("Spring.Http Tests")]
[assembly: AssemblyDescription("Unit tests for Spring.Http assembly")]

View File

@@ -0,0 +1,99 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Converters
{
/// <summary>
/// Unit tests for the ByteArrayHttpMessageConverter class.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
[TestFixture]
public class ByteArrayHttpMessageConverterTests
{
private ByteArrayHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new ByteArrayHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(byte[]), new MediaType("application", "octet-stream")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanWrite(typeof(byte[]), new MediaType("application", "octet-stream")));
Assert.IsTrue(converter.CanWrite(typeof(byte[]), MediaType.ALL));
}
[Test]
public void Read()
{
byte[] body = new byte[] { 0x1, 0x2 };
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(body)).Repeat.Once();
Expect.Call<long>(webResponse.ContentLength).Return(2).Repeat.Once();
mocks.ReplayAll();
byte[] result = converter.Read<byte[]>(webResponse);
Assert.AreEqual(body.Length, result.Length, "Invalid result");
Assert.AreEqual(body[0], result[0], "Invalid result");
Assert.AreEqual(body[1], result[1], "Invalid result");
mocks.VerifyAll();
}
[Test]
public void Write()
{
byte[] body = new byte[] { 0x1, 0x2 };
HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
webRequest.Method = "POST";
converter.Write(body, null, webRequest);
using (Stream postStream = webRequest.GetRequestStream())
{
//Assert.AreEqual(body.Length, postStream.Length, "Invalid result");
}
Assert.AreEqual(new MediaType("application", "octet-stream"), MediaType.ParseMediaType(webRequest.ContentType), "Invalid content-type");
Assert.AreEqual(2, webRequest.ContentLength, "Invalid content-length");
}
}
}

View File

@@ -0,0 +1,126 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Converters
{
/// <summary>
/// Unit tests for the StringHttpMessageConverter class.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
[TestFixture]
public class StringHttpMessageConverterTests
{
private StringHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new StringHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(string), new MediaType("text", "plain")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanWrite(typeof(string), new MediaType("text", "plain")));
Assert.IsTrue(converter.CanWrite(typeof(string), MediaType.ALL));
}
[Test]
public void Read()
{
string body = "Hello Bruno Baïa";
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body))).Repeat.Once();
Expect.Call<string>(webResponse.CharacterSet).Return("utf-8").Repeat.Twice();
mocks.ReplayAll();
string result = converter.Read<string>(webResponse);
Assert.AreEqual(body, result, "Invalid result");
mocks.VerifyAll();
}
[Test]
public void WriteDefaultCharset()
{
string body = "H\u00e9llo W\u00f6rld";
string charSet = "ISO-8859-1";
Encoding charSetEncoding = Encoding.GetEncoding(charSet);
HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
webRequest.Method = "POST";
converter.Write(body, null, webRequest);
using (Stream postStream = webRequest.GetRequestStream())
{
//Assert.AreEqual(body.Length, postStream.Length, "Invalid result");
}
Assert.AreEqual(new MediaType("text", "plain", charSet), MediaType.ParseMediaType(webRequest.ContentType), "Invalid content-type");
Assert.AreEqual(charSetEncoding.GetBytes(body).Length, webRequest.ContentLength, "Invalid content-length");
//Assert.IsFalse(String.IsNullOrEmpty(webRequest.Headers[HttpRequestHeader.AcceptCharset]), "Invalid accept-charset");
}
[Test]
public void WriteUTF8()
{
string body = "H\u00e9llo W\u00f6rld";
string charSet = "UTF-8";
Encoding charSetEncoding = Encoding.GetEncoding(charSet);
MediaType mediaType = new MediaType("text", "plain", charSet);
HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
webRequest.Method = "POST";
converter.Write(body, mediaType, webRequest);
using (Stream postStream = webRequest.GetRequestStream())
{
//Assert.AreEqual(body.Length, postStream.Length, "Invalid result");
}
Assert.AreEqual(mediaType, MediaType.ParseMediaType(webRequest.ContentType), "Invalid content-type");
Assert.AreEqual(charSetEncoding.GetBytes(body).Length, webRequest.ContentLength, "Invalid content-length");
//Assert.IsFalse(String.IsNullOrEmpty(webRequest.Headers[HttpRequestHeader.AcceptCharset]), "Invalid accept-charset");
}
}
}

View File

@@ -0,0 +1,328 @@
#if NET_4_0
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using Spring.Http.Converters;
using Spring.Http.Converters.Xml;
using NUnit.Framework;
namespace Spring.Http.Rest.Xml
{
/// <summary>
/// Integration tests for the DataContractHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class DataContractHttpMessageConverterIntegrationTests
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(RestTemplateIntegrationTests));
#endregion
private WebServiceHost webServiceHost;
private string uri = "http://localhost:1337";
private RestTemplate template;
private MediaType contentType;
[SetUp]
public void SetUp()
{
template = new RestTemplate(uri);
template.MessageConverters = new List<IHttpMessageConverter>();
template.MessageConverters.Add(new StringHttpMessageConverter()); // for debugging purpose
contentType = new MediaType("application", "xml");
webServiceHost = new WebServiceHost(typeof(TestService), new Uri(uri));
webServiceHost.Open();
}
[TearDown]
public void TearDownClass()
{
webServiceHost.Close();
}
//[Test]
//public void GetString()
//{
// string result = template.GetForObject<string>("users");
// Assert.AreEqual("2", result, "Invalid content");
//}
[Test]
public void GetUser()
{
template.MessageConverters.Add(new DataContractHttpMessageConverter());
User result = template.GetForObject<User>("user/{id}", "1");
Assert.IsNotNull(result, "Invalid content");
Assert.AreEqual("1", result.ID, "Invalid content");
Assert.AreEqual("Bruno Baïa", result.Name, "Invalid content");
}
//[Test]
//public void GetStringDictionaryTemplateVariables()
//{
// IDictionary<string, string> uriVariables = new Dictionary<string, string>(1);
// uriVariables.Add("id", "2");
// string result = template.GetForObject<string>("user/{id}", uriVariables);
// Assert.AreEqual("Marie Baia", result, "Invalid content");
//}
//[Test]
//[ExpectedException(typeof(RestClientException),
// ExpectedMessage = "The server returned 'User with id '5' not found' with the status code 404 - NotFound.")]
//public void GetStringError()
//{
// string result = template.GetForObject<string>("user/{id}", "5");
//}
//[Test]
//public void GetStringForMessage()
//{
// HttpResponseMessage<string> result = template.GetForMessage<string>("user/{id}", "1");
// Assert.AreEqual("Bruno Baïa", result.Body, "Invalid content");
// Assert.AreEqual(contentType, MediaType.ParseMediaType(result.Headers[HttpResponseHeader.ContentType]), "Invalid content-type");
// Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
// Assert.AreEqual("OK", result.StatusDescription, "Invalid status description");
//}
//[Test]
//[ExpectedException(typeof(RestClientException),
// ExpectedMessage = "Could not extract response: no Content-Type found")]
//public void GetStringNoResponse()
//{
// string result = template.GetForObject<string>("/nothing");
//}
//[Test]
//public void HeadForHeaders()
//{
// WebHeaderCollection result = template.HeadForHeaders("head");
// Assert.AreEqual(new MediaType("text", "plain"), MediaType.ParseMediaType(result[HttpResponseHeader.ContentType]), "Invalid content-type");
//}
//[Test]
//public void PostStringForLocation()
//{
// Uri result = template.PostForLocation("user", "Lisa Baia");
// Assert.AreEqual(new Uri(new Uri(uri), "/user/3"), result, "Invalid location");
//}
//[Test]
//public void PostStringForMessage()
//{
// HttpResponseMessage<string> result = template.PostForMessage<string>("user", "Lisa Baia");
// Assert.AreEqual(new Uri(new Uri(uri), "/user/3"), new Uri(result.Headers[HttpResponseHeader.Location]), "Invalid location");
// Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
// Assert.AreEqual("User id '3' created with 'Lisa Baia'", result.StatusDescription, "Invalid status description");
// Assert.AreEqual("3", result.Body, "Invalid content");
//}
[Test]
public void PostStringForObject()
{
template.MessageConverters.Add(new DataContractHttpMessageConverter());
User user = new User() { Name = "Lisa Baia" };
User result = template.PostForObject<User>("user", user);
Assert.AreEqual("3", result.ID, "Invalid content");
Assert.AreEqual(user.Name, result.Name, "Invalid content");
}
//[Test]
//[ExpectedException(typeof(RestClientException),
// ExpectedMessage = "The server returned 'Content cannot be null or empty' with the status code 400 - BadRequest.")]
//public void PostStringForObjectWithError()
//{
// string result = template.PostForObject<string>("user", "");
//}
//[Test]
//public void Put()
//{
// string result = template.GetForObject<string>("user/1");
// Assert.AreEqual("Bruno Baïa", result, "Invalid content");
// template.Put("user/1", "Bruno Baia");
// result = template.GetForObject<string>("user/1");
// Assert.AreEqual("Bruno Baia", result, "Invalid content");
//}
//[Test]
//[ExpectedException(typeof(RestClientException),
// ExpectedMessage = "The server returned 'User id '4' does not exist' with the status code 400 - BadRequest.")]
//public void PutWithError()
//{
// template.Put("user/4", "Dinora Baia");
//}
//[Test]
//public void Delete()
//{
// string result = template.GetForObject<string>("users");
// Assert.AreEqual("2", result, "Invalid content");
// template.Delete("user/2");
// result = template.GetForObject<string>("users");
// Assert.AreEqual("1", result, "Invalid content");
//}
//[Test]
//[ExpectedException(typeof(RestClientException),
// ExpectedMessage = "The server returned 'User id '10' does not exist' with the status code 400 - BadRequest.")]
//public void DeleteWithError()
//{
// template.Delete("user/10");
//}
//[Test]
//public void OptionsForAllow()
//{
// IList<HttpMethod> result = template.OptionsForAllow("allow");
// Assert.AreEqual(3, result.Count, "Invalid response");
// Assert.IsTrue(result.Contains(HttpMethod.GET), "Invalid response");
// Assert.IsTrue(result.Contains(HttpMethod.HEAD), "Invalid response");
// Assert.IsTrue(result.Contains(HttpMethod.PUT), "Invalid response");
//}
//[Test]
//public void ExchangePost()
//{
// HttpResponseMessage<string> result = template.Exchange<string>(
// "user", new HttpRequestMessage("Maryse Baia", HttpMethod.POST));
// Assert.AreEqual("3", result.Body, "Invalid content");
// Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
// Assert.AreEqual("User id '3' created with 'Maryse Baia'", result.StatusDescription, "Invalid status description");
//}
//[Test]
//public void ExchangePut()
//{
// HttpResponseMessage<string> result = template.Exchange<string>(
// "user/1", new HttpRequestMessage("Bruno Baia", HttpMethod.PUT));
// Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
// Assert.AreEqual("User id '1' updated with 'Bruno Baia'", result.StatusDescription, "Invalid status description");
//}
//[Test]
//[ExpectedException(ExpectedMessage = "The server returned 'Not Found' with the status code 404 - NotFound.")]
//public void ClientError()
//{
// template.Execute<object>("clienterror", null, null);
//}
//[Test]
//[ExpectedException(ExpectedMessage = "The server returned 'Internal Server Error' with the status code 500 - InternalServerError.")]
//public void ServerError()
//{
// template.Execute<object>("servererror", null, null);
//}
#region REST test service
//[DataContract]
public class User
{
//[DataMember]
public string ID { get; set; }
//[DataMember]
public string Name { get; set; }
}
[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class TestService
{
private IList<User> users;
public TestService()
{
users = new List<User>();
users.Add(new User() { ID = "1", Name = "Bruno Baïa" });
users.Add(new User() { ID = "2", Name = "Marie Baia" });
}
[WebGet(UriTemplate = "user/{id}")]
public User GetUser(string id)
{
WebOperationContext context = WebOperationContext.Current;
foreach (User user in this.users)
{
if (user.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase))
{
return user;
}
}
context.OutgoingResponse.SetStatusAsNotFound(String.Format("User with id '{0}' not found", id));
return null;
}
[WebInvoke(UriTemplate = "user", Method = "POST")]
public User Post(User user)
{
WebOperationContext context = WebOperationContext.Current;
UriTemplateMatch match = context.IncomingRequest.UriTemplateMatch;
UriTemplate template = new UriTemplate("/user/{id}");
MediaType mediaType = MediaType.ParseMediaType(context.IncomingRequest.ContentType);
user.ID = (users.Count + 1).ToString(); // generate new ID
if (!String.IsNullOrEmpty(user.ID))
{
context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
context.OutgoingResponse.StatusDescription = "Content cannot be null or empty";
return user;
}
users.Add(user);
Uri uri = template.BindByPosition(match.BaseUri, user.ID);
context.OutgoingResponse.SetStatusAsCreated(uri);
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' created", user.ID);
return user;
}
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,119 @@
#if NET_3_0
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
using System.Xml;
using System.Runtime.Serialization;
namespace Spring.Http.Converters.Xml
{
/// <summary>
/// Unit tests for the DataContractHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class DataContractHttpMessageConverterTests
{
private DataContractHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new DataContractHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(CustomClass), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(CustomClass), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(CustomClass), new MediaType("application", "soap+xml"))); // application/*+xml
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanWrite(typeof(CustomClass), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanWrite(typeof(CustomClass), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(CustomClass), new MediaType("application", "soap+xml"))); // application/*+xml
}
//[Test]
//public void Read()
//{
// string body = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><TestElement testAttribute=\"value\" />";
// HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
// Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body))).Repeat.Once();
// mocks.ReplayAll();
// XmlDocument result = converter.Read<XmlDocument>(webResponse);
// Assert.IsNotNull(result, "Invalid result");
// mocks.VerifyAll();
//}
//[Test]
//public void Write()
//{
// XmlDocument body = new XmlDocument();
// body.CreateElement("TestElement");
// HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
// webRequest.Method = "POST";
// converter.Write(body, null, webRequest);
// Assert.AreEqual(new MediaType("application", "xml"), MediaType.ParseMediaType(webRequest.ContentType), "Invalid content-type");
// using (Stream postStream = webRequest.GetRequestStream())
// {
// using (StreamReader reader = new StreamReader(postStream))
// {
// string result = reader.ReadToEnd();
// Assert.AreEqual(result.Length, webRequest.ContentLength, "Invalid content-length");
// }
// }
//}
#region Test classes
[DataContract]
public class CustomClass
{
[DataMember]
public string ID { get; set; }
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,105 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
using System.Xml;
namespace Spring.Http.Converters.Xml
{
/// <summary>
/// Unit tests for the XmlDocumentHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class XmlDocumentHttpMessageConverterTests
{
private XmlDocumentHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new XmlDocumentHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(XmlDocument), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(XmlDocument), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(XmlDocument), new MediaType("application", "soap+xml"))); // application/*+xml
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanWrite(typeof(XmlDocument), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanWrite(typeof(XmlDocument), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(XmlDocument), new MediaType("application", "soap+xml"))); // application/*+xml
}
[Test]
public void Read()
{
string body = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><TestElement testAttribute=\"value\" />";
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body))).Repeat.Once();
mocks.ReplayAll();
XmlDocument result = converter.Read<XmlDocument>(webResponse);
Assert.IsNotNull(result, "Invalid result");
mocks.VerifyAll();
}
[Test]
public void Write()
{
XmlDocument body = new XmlDocument();
body.CreateElement("TestElement");
HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
webRequest.Method = "POST";
converter.Write(body, null, webRequest);
Assert.AreEqual(new MediaType("application", "xml"), MediaType.ParseMediaType(webRequest.ContentType), "Invalid content-type");
using (Stream postStream = webRequest.GetRequestStream())
{
using (StreamReader reader = new StreamReader(postStream))
{
string result = reader.ReadToEnd();
Assert.AreEqual(result.Length, webRequest.ContentLength, "Invalid content-length");
}
}
}
}
}

View File

@@ -0,0 +1,544 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Spring.Http
{
/// <summary>
/// Unit tests for the MediaType class.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Juergen Hoeller</author>
/// <author>Bruno Baia (.NET)</author>
[TestFixture]
public class MediaTypeTests
{
[Test]
public void Includes()
{
MediaType textPlain = MediaType.TEXT_PLAIN;
Assert.IsTrue(textPlain.Includes(textPlain), "Equal types is not inclusive");
MediaType allText = new MediaType("text");
Assert.IsTrue(allText.Includes(textPlain), "All subtypes is not inclusive");
Assert.IsFalse(textPlain.Includes(allText), "All subtypes is inclusive");
Assert.IsTrue(MediaType.ALL.Includes(textPlain), "All types is not inclusive");
Assert.IsFalse(textPlain.Includes(MediaType.ALL), "All types is inclusive");
Assert.IsTrue(MediaType.ALL.Includes(textPlain), "All types is not inclusive");
Assert.IsFalse(textPlain.Includes(MediaType.ALL), "All types is inclusive");
MediaType applicationSoapXml = new MediaType("application", "soap+xml");
MediaType applicationWildcardXml = new MediaType("application", "*+xml");
Assert.IsTrue(applicationSoapXml.Includes(applicationSoapXml));
Assert.IsTrue(applicationWildcardXml.Includes(applicationWildcardXml));
Assert.IsTrue(applicationWildcardXml.Includes(applicationSoapXml));
Assert.IsFalse(applicationSoapXml.Includes(applicationWildcardXml));
}
[Test]
public void IsCompatible()
{
MediaType textPlain = MediaType.TEXT_PLAIN;
Assert.IsTrue(textPlain.IsCompatibleWith(textPlain), "Equal types is not compatible");
MediaType allText = new MediaType("text");
Assert.IsTrue(allText.IsCompatibleWith(textPlain), "All subtypes is not compatible");
Assert.IsTrue(textPlain.IsCompatibleWith(allText), "All subtypes is not compatible");
Assert.IsTrue(MediaType.ALL.IsCompatibleWith(textPlain), "All types is not compatible");
Assert.IsTrue(textPlain.IsCompatibleWith(MediaType.ALL), "All types is not compatible");
Assert.IsTrue(MediaType.ALL.IsCompatibleWith(textPlain), "All types is not compatible");
Assert.IsTrue(textPlain.IsCompatibleWith(MediaType.ALL), "All types is compatible");
MediaType applicationSoapXml = new MediaType("application", "soap+xml");
MediaType applicationWildcardXml = new MediaType("application", "*+xml");
Assert.IsTrue(applicationSoapXml.IsCompatibleWith(applicationSoapXml));
Assert.IsTrue(applicationWildcardXml.IsCompatibleWith(applicationWildcardXml));
Assert.IsTrue(applicationWildcardXml.IsCompatibleWith(applicationSoapXml));
Assert.IsTrue(applicationSoapXml.IsCompatibleWith(applicationWildcardXml));
}
[Test]
public void TestToString()
{
MediaType mediaType = new MediaType("text", "plain", 0.7);
String result = mediaType.ToString();
Assert.AreEqual("text/plain;q=0.7", result, "Invalid toString() returned");
}
//[Test](expected= IllegalArgumentException.class)
//public void slashInType() {
// new MediaType("text/plain");
//}
//[Test](expected= IllegalArgumentException.class)
//public void slashInSubtype() {
// new MediaType("text", "/");
//}
[Test]
public void GetDefaultQualityValue()
{
MediaType mediaType = new MediaType("text", "plain");
Assert.AreEqual(1, mediaType.QualityValue, "Invalid quality value");
}
[Test]
public void ParseMediaType()
{
string s = "audio/*; q=0.2";
MediaType mediaType = MediaType.ParseMediaType(s);
Assert.AreEqual("audio", mediaType.Type, "Invalid type");
Assert.AreEqual("*", mediaType.Subtype, "Invalid subtype");
Assert.AreEqual(0.2, mediaType.QualityValue, "Invalid quality factor");
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void ParseMediaTypeNoSubtype()
{
MediaType.ParseMediaType("audio");
}
[Test]
[ExpectedException(typeof(ArgumentException))]
public void ParseMediaTypeNoSubtypeSlash()
{
MediaType.ParseMediaType("audio/");
}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeIllegalType() {
// MediaType.parseMediaType("audio(/basic");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeIllegalSubtype() {
// MediaType.parseMediaType("audio/basic)");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeEmptyParameterAttribute() {
// MediaType.parseMediaType("audio/*;=value");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeEmptyParameterValue() {
// MediaType.parseMediaType("audio/*;attr=");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeIllegalParameterAttribute() {
// MediaType.parseMediaType("audio/*;attr<=value");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeIllegalParameterValue() {
// MediaType.parseMediaType("audio/*;attr=v>alue");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeIllegalQualityFactor() {
// MediaType.parseMediaType("audio/basic;q=1.1");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeIllegalCharset() {
// MediaType.parseMediaType("text/html; charset=foo-bar");
//}
//[Test]
//public void parseMediaTypeQuotedParameterValue() {
// MediaType.parseMediaType("audio/*;attr=\"v>alue\"");
//}
//[Test](expected = IllegalArgumentException.class)
//public void parseMediaTypeIllegalQuotedParameterValue() {
// MediaType.parseMediaType("audio/*;attr=\"");
//}
//[Test]
//public void parseCharset() throws Exception {
// String s = "text/html; charset=iso-8859-1";
// MediaType mediaType = MediaType.parseMediaType(s);
// Assert.AreEqual("Invalid type", "text", mediaType.getType());
// Assert.AreEqual("Invalid subtype", "html", mediaType.getSubtype());
// Assert.AreEqual("Invalid charset", Charset.forName("ISO-8859-1"), mediaType.getCharSet());
//}
//[Test]
//public void parseQuotedCharset() {
// String s = "application/xml;charset=\"utf-8\"";
// MediaType mediaType = MediaType.parseMediaType(s);
// Assert.AreEqual("Invalid type", "application", mediaType.getType());
// Assert.AreEqual("Invalid subtype", "xml", mediaType.getSubtype());
// Assert.AreEqual("Invalid charset", Charset.forName("UTF-8"), mediaType.getCharSet());
//}
[Test]
public void ParseURLConnectionMediaType()
{
string s = "*; q=.2";
MediaType mediaType = MediaType.ParseMediaType(s);
Assert.AreEqual(mediaType.Type, "*", "Invalid type");
Assert.AreEqual("*", mediaType.Subtype, "Invalid subtype");
Assert.AreEqual(0.2, mediaType.QualityValue, "Invalid quality factor");
}
[Test]
public void ParseMediaTypes()
{
string s = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c";
List<MediaType> mediaTypes = MediaType.ParseMediaTypes(s);
Assert.NotNull(mediaTypes, "No media types returned");
Assert.AreEqual(4, mediaTypes.Count, "Invalid amount of media types");
mediaTypes = MediaType.ParseMediaTypes(null);
Assert.NotNull(mediaTypes, "No media types returned");
Assert.AreEqual(0, mediaTypes.Count, "Invalid amount of media types");
}
[Test]
public void CompareTo()
{
MediaType audioBasic = new MediaType("audio", "basic");
MediaType audio = new MediaType("audio");
MediaType audioWave = new MediaType("audio", "wave");
MediaType audioBasicLevel = new MediaType("audio", "basic", SingletonDictionary<string, string>("level", "1"));
MediaType audioBasic07 = new MediaType("audio", "basic", 0.7);
// equal
Assert.AreEqual(0, audioBasic.CompareTo(audioBasic), "Invalid comparison result");
Assert.AreEqual(0, audio.CompareTo(audio), "Invalid comparison result");
Assert.AreEqual(0, audioBasicLevel.CompareTo(audioBasicLevel), "Invalid comparison result");
Assert.IsTrue(audioBasicLevel.CompareTo(audio) > 0, "Invalid comparison result");
List<MediaType> expected = new List<MediaType>();
expected.Add(audio);
expected.Add(audioBasic);
expected.Add(audioBasicLevel);
expected.Add(audioBasic07);
expected.Add(audioWave);
List<MediaType> result = new List<MediaType>(expected);
// shuffle & sort 10 times
for (int i = 0; i < 10; i++)
{
result.Sort(ShuffleComparison);
result.Sort();
for (int j = 0; j < result.Count; j++)
{
Assert.AreSame(expected[j], result[j], "Invalid media type at " + j + ", run " + i);
}
}
}
[Test]
public void CompareToConsistentWithEquals()
{
MediaType m1 = MediaType.ParseMediaType("text/html; q=0.7; charset=iso-8859-1");
MediaType m2 = MediaType.ParseMediaType("text/html; charset=iso-8859-1; q=0.7");
Assert.AreEqual(m1, m2, "Media types not equal");
Assert.AreEqual(0, m1.CompareTo(m2), "compareTo() not consistent with equals");
Assert.AreEqual(0, m2.CompareTo(m1), "compareTo() not consistent with equals");
m1 = MediaType.ParseMediaType("text/html; q=0.7; charset=iso-8859-1");
m2 = MediaType.ParseMediaType("text/html; Q=0.7; charset=iso-8859-1");
Assert.AreEqual(m1, m2, "Media types not equal");
Assert.AreEqual(0, m1.CompareTo(m2), "compareTo() not consistent with equals");
Assert.AreEqual(0, m2.CompareTo(m1), "compareTo() not consistent with equals");
}
[Test]
public void CompareToCaseSensitivity()
{
MediaType m1 = new MediaType("audio", "basic");
MediaType m2 = new MediaType("Audio", "Basic");
Assert.AreEqual(0, m1.CompareTo(m2), "Invalid comparison result");
Assert.AreEqual(0, m2.CompareTo(m1), "Invalid comparison result");
m1 = new MediaType("audio", "basic", SingletonDictionary<string, string>("foo", "bar"));
m2 = new MediaType("audio", "basic", SingletonDictionary<string, string>("Foo", "bar"));
Assert.AreEqual(0, m1.CompareTo(m2), "Invalid comparison result");
Assert.AreEqual(0, m2.CompareTo(m1), "Invalid comparison result");
m1 = new MediaType("audio", "basic", SingletonDictionary<string, string>("foo", "bar"));
m2 = new MediaType("audio", "basic", SingletonDictionary<string, string>("foo", "Bar"));
Assert.IsTrue(m1.CompareTo(m2) != 0, "Invalid comparison result");
Assert.IsTrue(m2.CompareTo(m1) != 0, "Invalid comparison result");
}
[Test]
public void SpecificityComparator()
{
MediaType audioBasic = new MediaType("audio", "basic");
MediaType audioWave = new MediaType("audio", "wave");
MediaType audio = new MediaType("audio");
MediaType audio03 = new MediaType("audio", "*", 0.3);
MediaType audio07 = new MediaType("audio", "*", 0.7);
MediaType audioBasicLevel = new MediaType("audio", "basic", SingletonDictionary<string, string>("level", "1"));
MediaType textHtml = new MediaType("text", "html");
MediaType all = MediaType.ALL;
IComparer<MediaType> comp = MediaType.SPECIFICITY_COMPARER;
// equal
Assert.AreEqual(0, comp.Compare(audioBasic,audioBasic), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audio, audio), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audio07, audio07), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audio03, audio03), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audioBasicLevel, audioBasicLevel), "Invalid comparison result");
// specific to unspecific
Assert.IsTrue(comp.Compare(audioBasic, audio) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audioBasic, all) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio, all) < 0, "Invalid comparison result");
// unspecific to specific
Assert.IsTrue(comp.Compare(audio, audioBasic) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(all, audioBasic) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(all, audio) > 0, "Invalid comparison result");
// qualifiers
Assert.IsTrue(comp.Compare(audio, audio07) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio07, audio) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio07, audio03) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio03, audio07) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio03, all) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(all, audio03) > 0, "Invalid comparison result");
// other parameters
Assert.IsTrue(comp.Compare(audioBasic, audioBasicLevel) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audioBasicLevel, audioBasic) < 0, "Invalid comparison result");
// different types
Assert.AreEqual(0, comp.Compare(audioBasic, textHtml), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(textHtml, audioBasic), "Invalid comparison result");
// different subtypes
Assert.AreEqual(0, comp.Compare(audioBasic, audioWave), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audioWave, audioBasic), "Invalid comparison result");
}
[Test]
public void SortBySpecificityRelated()
{
MediaType audioBasic = new MediaType("audio", "basic");
MediaType audio = new MediaType("audio");
MediaType audio03 = new MediaType("audio", "*", 0.3);
MediaType audio07 = new MediaType("audio", "*", 0.7);
MediaType audioBasicLevel = new MediaType("audio", "basic", SingletonDictionary<string, string>("level", "1"));
MediaType all = MediaType.ALL;
List<MediaType> expected = new List<MediaType>();
expected.Add(audioBasicLevel);
expected.Add(audioBasic);
expected.Add(audio);
expected.Add(audio07);
expected.Add(audio03);
expected.Add(all);
List<MediaType> result = new List<MediaType>(expected);
// shuffle & sort 10 times
for (int i = 0; i < 10; i++)
{
result.Sort(ShuffleComparison);
MediaType.SortBySpecificity(result);
for (int j = 0; j < result.Count; j++)
{
Assert.AreSame(expected[j], result[j], "Invalid media type at " + j);
}
}
}
[Test]
[Ignore]
public void SortBySpecificityUnrelated()
{
MediaType audioBasic = new MediaType("audio", "basic");
MediaType audioWave = new MediaType("audio", "wave");
MediaType textHtml = new MediaType("text", "html");
List<MediaType> expected = new List<MediaType>();
expected.Add(textHtml);
expected.Add(audioBasic);
expected.Add(audioWave);
List<MediaType> result = new List<MediaType>(expected);
MediaType.SortBySpecificity(result);
for (int i = 0; i < result.Count; i++)
{
Assert.AreSame(expected[i], result[i], "Invalid media type at " + i);
}
}
[Test]
public void QualityComparator()
{
MediaType audioBasic = new MediaType("audio", "basic");
MediaType audioWave = new MediaType("audio", "wave");
MediaType audio = new MediaType("audio");
MediaType audio03 = new MediaType("audio", "*", 0.3);
MediaType audio07 = new MediaType("audio", "*", 0.7);
MediaType audioBasicLevel = new MediaType("audio", "basic", SingletonDictionary<string, string>("level", "1"));
MediaType textHtml = new MediaType("text", "html");
MediaType all = MediaType.ALL;
IComparer<MediaType> comp = MediaType.QUALITY_VALUE_COMPARER;
// equal
Assert.AreEqual(0, comp.Compare(audioBasic, audioBasic), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audio, audio), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audio07, audio07), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audio03, audio03), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audioBasicLevel, audioBasicLevel), "Invalid comparison result");
// specific to unspecific
Assert.IsTrue(comp.Compare(audioBasic, audio) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audioBasic, all) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio, all) < 0, "Invalid comparison result");
// unspecific to specific
Assert.IsTrue(comp.Compare(audio, audioBasic) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(all, audioBasic) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(all, audio) > 0, "Invalid comparison result");
// qualifiers
Assert.IsTrue(comp.Compare(audio, audio07) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio07, audio) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio07, audio03) < 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio03, audio07) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audio03, all) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(all, audio03) < 0, "Invalid comparison result");
// other parameters
Assert.IsTrue(comp.Compare(audioBasic, audioBasicLevel) > 0, "Invalid comparison result");
Assert.IsTrue(comp.Compare(audioBasicLevel, audioBasic) < 0, "Invalid comparison result");
// different types
Assert.AreEqual(0, comp.Compare(audioBasic, textHtml), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(textHtml, audioBasic), "Invalid comparison result");
// different subtypes
Assert.AreEqual(0, comp.Compare(audioBasic, audioWave), "Invalid comparison result");
Assert.AreEqual(0, comp.Compare(audioWave, audioBasic), "Invalid comparison result");
}
[Test]
public void SortByQualityRelated()
{
MediaType audioBasic = new MediaType("audio", "basic");
MediaType audio = new MediaType("audio");
MediaType audio03 = new MediaType("audio", "*", 0.3);
MediaType audio07 = new MediaType("audio", "*", 0.7);
MediaType audioBasicLevel = new MediaType("audio", "basic", SingletonDictionary<string, string>("level", "1"));
MediaType all = MediaType.ALL;
List<MediaType> expected = new List<MediaType>();
expected.Add(audioBasicLevel);
expected.Add(audioBasic);
expected.Add(audio);
expected.Add(all);
expected.Add(audio07);
expected.Add(audio03);
List<MediaType> result = new List<MediaType>(expected);
// shuffle & sort 10 times
for (int i = 0; i < 10; i++)
{
result.Sort(ShuffleComparison);
MediaType.SortByQualityValue(result);
for (int j = 0; j < result.Count; j++)
{
Assert.AreSame(expected[j], result[j], "Invalid media type at " + j);
}
}
}
[Test]
[Ignore]
public void SortByQualityUnrelated()
{
MediaType audioBasic = new MediaType("audio", "basic");
MediaType audioWave = new MediaType("audio", "wave");
MediaType textHtml = new MediaType("text", "html");
List<MediaType> expected = new List<MediaType>();
expected.Add(textHtml);
expected.Add(audioBasic);
expected.Add(audioWave);
List<MediaType> result = new List<MediaType>(expected);
MediaType.SortBySpecificity(result);
for (int i = 0; i < result.Count; i++)
{
Assert.AreSame(expected[i], result[i], "Invalid media type at " + i);
}
}
//[Test]
//public void testWithConversionService() {
// ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
// Assert.IsTrue(conversionService.canConvert(String.class, MediaType.class));
// MediaType mediaType = MediaType.parseMediaType("application/xml");
// Assert.AreEqual(mediaType, conversionService.convert("application/xml", MediaType.class));
//}
#region Utils
private static int ShuffleComparison(MediaType mediaType1, MediaType mediaType2)
{
if (mediaType1 == mediaType2)
{
return 0;
}
Random rd = new Random();
return rd.Next(-1, 2);
}
private static IDictionary<TKey, TValue> SingletonDictionary<TKey, TValue>(TKey key, TValue value)
{
IDictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>(1);
dictionary.Add(key, value);
return dictionary;
}
#endregion
}
}

View File

@@ -0,0 +1,486 @@
#if NET_4_0
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Channels;
using Spring.Http;
using Spring.Http.Rest.Support;
using NUnit.Framework;
namespace Spring.Http.Rest
{
/// <summary>
/// Integration tests for the RestTemplate class.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
[TestFixture]
public class RestTemplateIntegrationTests
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(RestTemplateIntegrationTests));
#endregion
private WebServiceHost webServiceHost;
private string uri = "http://localhost:1337";
private RestTemplate template;
private MediaType contentType;
[SetUp]
public void SetUp()
{
template = new RestTemplate(uri);
contentType = new MediaType("text", "plain");
webServiceHost = new WebServiceHost(typeof(TestService), new Uri(uri));
webServiceHost.Open();
}
[TearDown]
public void TearDownClass()
{
webServiceHost.Close();
}
[Test]
public void GetString()
{
string result = template.GetForObject<string>("users");
Assert.AreEqual("2", result, "Invalid content");
}
[Test]
public void GetStringVarArgsTemplateVariables()
{
string result = template.GetForObject<string>("user/{id}", "1");
Assert.AreEqual("Bruno Baïa", result, "Invalid content");
}
[Test]
public void GetStringDictionaryTemplateVariables()
{
IDictionary<string, string> uriVariables = new Dictionary<string, string>(1);
uriVariables.Add("id", "2");
string result = template.GetForObject<string>("user/{id}", uriVariables);
Assert.AreEqual("Marie Baia", result, "Invalid content");
}
[Test]
[ExpectedException(typeof(RestClientException),
ExpectedMessage = "The server returned 'User with id '5' not found' with the status code 404 - NotFound.")]
public void GetStringError()
{
string result = template.GetForObject<string>("user/{id}", "5");
}
[Test]
public void GetStringForMessage()
{
HttpResponseMessage<string> result = template.GetForMessage<string>("user/{id}", "1");
Assert.AreEqual("Bruno Baïa", result.Body, "Invalid content");
Assert.AreEqual(contentType, MediaType.ParseMediaType(result.Headers[HttpResponseHeader.ContentType]), "Invalid content-type");
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
Assert.AreEqual("OK", result.StatusDescription, "Invalid status description");
}
[Test]
[ExpectedException(typeof(RestClientException),
ExpectedMessage = "Could not extract response: no Content-Type found")]
public void GetStringNoResponse()
{
string result = template.GetForObject<string>("/nothing");
}
[Test]
public void HeadForHeaders()
{
WebHeaderCollection result = template.HeadForHeaders("head");
Assert.AreEqual(new MediaType("text", "plain"), MediaType.ParseMediaType(result[HttpResponseHeader.ContentType]), "Invalid content-type");
}
[Test]
public void PostStringForLocation()
{
Uri result = template.PostForLocation("user", "Lisa Baia");
Assert.AreEqual(new Uri(new Uri(uri), "/user/3"), result, "Invalid location");
}
[Test]
public void PostStringForMessage()
{
HttpResponseMessage<string> result = template.PostForMessage<string>("user", "Lisa Baia");
Assert.AreEqual(new Uri(new Uri(uri), "/user/3"), new Uri(result.Headers[HttpResponseHeader.Location]), "Invalid location");
Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
Assert.AreEqual("User id '3' created with 'Lisa Baia'", result.StatusDescription, "Invalid status description");
Assert.AreEqual("3", result.Body, "Invalid content");
}
[Test]
public void PostStringForObject()
{
string result = template.PostForObject<string>("user", "Lisa Baia");
Assert.AreEqual("3", result, "Invalid content");
}
[Test]
[ExpectedException(typeof(RestClientException),
ExpectedMessage = "The server returned 'Content cannot be null or empty' with the status code 400 - BadRequest.")]
public void PostStringForObjectWithError()
{
string result = template.PostForObject<string>("user", "");
}
[Test]
public void Put()
{
string result = template.GetForObject<string>("user/1");
Assert.AreEqual("Bruno Baïa", result, "Invalid content");
template.Put("user/1", "Bruno Baia");
result = template.GetForObject<string>("user/1");
Assert.AreEqual("Bruno Baia", result, "Invalid content");
}
[Test]
[ExpectedException(typeof(RestClientException),
ExpectedMessage = "The server returned 'User id '4' does not exist' with the status code 400 - BadRequest.")]
public void PutWithError()
{
template.Put("user/4", "Dinora Baia");
}
[Test]
public void Delete()
{
string result = template.GetForObject<string>("users");
Assert.AreEqual("2", result, "Invalid content");
template.Delete("user/2");
result = template.GetForObject<string>("users");
Assert.AreEqual("1", result, "Invalid content");
}
[Test]
[ExpectedException(typeof(RestClientException),
ExpectedMessage = "The server returned 'User id '10' does not exist' with the status code 400 - BadRequest.")]
public void DeleteWithError()
{
template.Delete("user/10");
}
[Test]
public void OptionsForAllow()
{
IList<HttpMethod> result = template.OptionsForAllow("allow");
Assert.AreEqual(3, result.Count, "Invalid response");
Assert.IsTrue(result.Contains(HttpMethod.GET), "Invalid response");
Assert.IsTrue(result.Contains(HttpMethod.HEAD), "Invalid response");
Assert.IsTrue(result.Contains(HttpMethod.PUT), "Invalid response");
}
[Test]
public void ExchangePost()
{
HttpResponseMessage<string> result = template.Exchange<string>(
"user", new HttpRequestMessage("Maryse Baia", HttpMethod.POST));
Assert.AreEqual("3", result.Body, "Invalid content");
Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
Assert.AreEqual("User id '3' created with 'Maryse Baia'", result.StatusDescription, "Invalid status description");
}
[Test]
public void ExchangePut()
{
HttpResponseMessage<string> result = template.Exchange<string>(
"user/1", new HttpRequestMessage("Bruno Baia", HttpMethod.PUT));
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
Assert.AreEqual("User id '1' updated with 'Bruno Baia'", result.StatusDescription, "Invalid status description");
}
[Test]
[ExpectedException(ExpectedMessage = "The server returned 'Not Found' with the status code 404 - NotFound.")]
public void ClientError()
{
template.Execute<object>("clienterror", null, null);
}
[Test]
[ExpectedException(ExpectedMessage = "The server returned 'Internal Server Error' with the status code 500 - InternalServerError.")]
public void ServerError()
{
template.Execute<object>("servererror", null, null);
}
//@BeforeClass
//public static void startJettyServer() throws Exception {
// jettyServer = new Server(8889);
// Context jettyContext = new Context(jettyServer, "/");
// byte[] bytes = helloWorld.getBytes("UTF-8");
// contentType = new MediaType("text", "plain", Collections.singletonMap("charset", "utf-8"));
// jettyContext.addServlet(new ServletHolder(new GetServlet(bytes, contentType)), "/get");
// jettyContext.addServlet(new ServletHolder(new GetServlet(new byte[0], contentType)), "/get/nothing");
// jettyContext.addServlet(
// new ServletHolder(new PostServlet(helloWorld, URI + "/post/1", bytes, contentType)),
// "/post");
// jettyContext.addServlet(new ServletHolder(new ErrorServlet(404)), "/errors/notfound");
// jettyContext.addServlet(new ServletHolder(new ErrorServlet(500)), "/errors/server");
// jettyContext.addServlet(new ServletHolder(new UriServlet()), "/uri/*");
// jettyContext.addServlet(new ServletHolder(new MultipartServlet()), "/multipart");
// jettyServer.start();
//}
//@Test
//public void uri() throws InterruptedException, URISyntaxException {
// String result = template.getForObject(URI + "/uri/{query}", String.class, "Z\u00fcrich");
// Assert.AreEqual("Invalid request URI", "/uri/Z%C3%BCrich", result);
// result = template.getForObject(URI + "/uri/query={query}", String.class, "foo@bar");
// Assert.AreEqual("Invalid request URI", "/uri/query=foo@bar", result);
// result = template.getForObject(URI + "/uri/query={query}", String.class, "T\u014dky\u014d");
// Assert.AreEqual("Invalid request URI", "/uri/query=T%C5%8Dky%C5%8D", result);
//}
//@Test
//public void multipart() throws UnsupportedEncodingException {
// MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
// parts.add("name 1", "value 1");
// parts.add("name 2", "value 2+1");
// parts.add("name 2", "value 2+2");
// Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg");
// parts.add("logo", logo);
// template.postForLocation(URI + "/multipart", parts);
//}
//private static class UriServlet extends HttpServlet {
// @Override
// protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// resp.setContentType("text/plain");
// resp.setCharacterEncoding("UTF-8");
// resp.getWriter().write(req.getRequestURI());
// }
//}
//private static class MultipartServlet extends HttpServlet {
// @Override
// protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// assertTrue(ServletFileUpload.isMultipartContent(req));
// FileItemFactory factory = new DiskFileItemFactory();
// ServletFileUpload upload = new ServletFileUpload(factory);
// try {
// List items = upload.parseRequest(req);
// Assert.AreEqual(4, items.size());
// FileItem item = (FileItem) items.get(0);
// assertTrue(item.isFormField());
// Assert.AreEqual("name 1", item.getFieldName());
// Assert.AreEqual("value 1", item.getString());
// item = (FileItem) items.get(1);
// assertTrue(item.isFormField());
// Assert.AreEqual("name 2", item.getFieldName());
// Assert.AreEqual("value 2+1", item.getString());
// item = (FileItem) items.get(2);
// assertTrue(item.isFormField());
// Assert.AreEqual("name 2", item.getFieldName());
// Assert.AreEqual("value 2+2", item.getString());
// item = (FileItem) items.get(3);
// Assert.IsFalse(item.isFormField());
// Assert.AreEqual("logo", item.getFieldName());
// Assert.AreEqual("logo.jpg", item.getName());
// Assert.AreEqual("image/jpeg", item.getContentType());
// }
// catch (FileUploadException ex) {
// throw new ServletException(ex);
// }
// }
//}
#region REST test service
[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class TestService
{
private IDictionary<string, string> users;
public TestService()
{
users = new Dictionary<string, string>();
users.Add("1", "Bruno Baïa");
users.Add("2", "Marie Baia");
}
[WebGet(UriTemplate = "clienterror")]
public void ClientError()
{
WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
}
[WebGet(UriTemplate = "servererror")]
public void ServerError()
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
}
[WebInvoke(UriTemplate = "allow", Method = "OPTIONS")]
public void Allow()
{
WebOperationContext.Current.OutgoingResponse.Headers[HttpResponseHeader.Allow] = "GET, HEAD, PUT";
}
[WebInvoke(UriTemplate = "head", Method = "HEAD")]
public void Head()
{
WebOperationContext.Current.OutgoingResponse.Headers[HttpResponseHeader.ContentType] = "text/plain";
}
[WebGet(UriTemplate = "user/{id}")]
public Message GetUser(string id)
{
WebOperationContext context = WebOperationContext.Current;
if (!users.ContainsKey(id))
{
context.OutgoingResponse.SetStatusAsNotFound(String.Format("User with id '{0}' not found", id));
return context.CreateTextResponse(null);
}
return context.CreateTextResponse(users[id]);
}
[WebGet(UriTemplate = "users")]
public Message GetUsersCount()
{
WebOperationContext context = WebOperationContext.Current;
return context.CreateTextResponse(users.Count.ToString());
}
[WebGet(UriTemplate = "nothing")]
public void GetNothing()
{
WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = true;
}
[WebInvoke(UriTemplate = "user", Method = "POST")]
public Message Post(Stream stream)
{
WebOperationContext context = WebOperationContext.Current;
UriTemplateMatch match = context.IncomingRequest.UriTemplateMatch;
UriTemplate template = new UriTemplate("/user/{id}");
MediaType mediaType = MediaType.ParseMediaType(context.IncomingRequest.ContentType);
string id = (users.Count + 1).ToString(); // generate new ID
string name;
using (StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(mediaType.CharSet)))
{
name = reader.ReadToEnd();
}
if (String.IsNullOrEmpty(name))
{
context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
context.OutgoingResponse.StatusDescription = "Content cannot be null or empty";
return WebOperationContext.Current.CreateTextResponse("");
}
users.Add(id, name);
Uri uri = template.BindByPosition(match.BaseUri, id);
context.OutgoingResponse.SetStatusAsCreated(uri);
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' created with '{1}'", id, name);
return WebOperationContext.Current.CreateTextResponse(id);
}
[WebInvoke(UriTemplate = "user/{id}", Method = "PUT")]
public void Update(string id, Stream stream)
{
WebOperationContext context = WebOperationContext.Current;
if (!users.ContainsKey(id))
{
context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' does not exist", id);
return;
}
MediaType mediaType = MediaType.ParseMediaType(context.IncomingRequest.ContentType);
string name;
using (StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(mediaType.CharSet)))
{
name = reader.ReadToEnd();
}
users[id] = name;
context.OutgoingResponse.StatusCode = HttpStatusCode.OK;
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' updated with '{1}'", id, name);
//context.OutgoingResponse.ContentType = "text/plain";
}
[WebInvoke(UriTemplate = "user/{id}", Method = "DELETE")]
public void Delete(string id)
{
WebOperationContext context = WebOperationContext.Current;
if (!users.ContainsKey(id))
{
context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' does not exist", id);
return;
}
users.Remove(id);
context.OutgoingResponse.StatusCode = HttpStatusCode.OK;
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' have been removed", id);
}
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,643 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Net;
using System.Collections.Generic;
using Spring.Http;
using Spring.Http.Converters;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Rest
{
/// <summary>
/// Unit tests for the RestTemplate class.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
[TestFixture]
public class RestTemplateTests
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(RestTemplateTests));
#endregion
private MockRepository mocks;
private RestTemplate template;
private IHttpWebRequestFactory requestFactory;
private HttpWebRequest request;
private HttpWebResponse response;
//private ResponseErrorHandler errorHandler;
private IHttpMessageConverter converter;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
requestFactory = mocks.CreateMock<IHttpWebRequestFactory>();
request = mocks.CreateMock<HttpWebRequest>();
response = mocks.CreateMock<HttpWebResponse>();
//errorHandler = createMock(ResponseErrorHandler.class);
converter = mocks.CreateMock<IHttpMessageConverter>();
IList<IHttpMessageConverter> messageConverters = new List<IHttpMessageConverter>(1);
messageConverters.Add(converter);
template = new RestTemplate();
template.RequestFactory = requestFactory;
template.MessageConverters = messageConverters;
//template.setErrorHandler(errorHandler);
}
[TearDown]
public void TearDown()
{
mocks.VerifyAll();
}
[Test]
public void VarArgsTemplateVariables()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com/hotels/42/bookings/21")))
.Return(request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
mocks.ReplayAll();
template.Execute<object>("http://example.com/hotels/{hotel}/bookings/{booking}", null, null, "42", "21");
}
[Test]
public void DictionaryTemplateVariables()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com/hotels/42/bookings/21")))
.Return(request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
mocks.ReplayAll();
IDictionary<string, string> variables = new Dictionary<string, string>();
variables.Add("booking", "41");
variables.Add("hotel", "42");
template.Execute<object>("http://example.com/hotels/{hotel}/bookings/{booking}", null, null, "42", "21");
}
[Test]
public void BaseAddressTemplate()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com/hotels/42/bookings/21")))
.Return(request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
mocks.ReplayAll();
template.BaseAddress = new Uri("http://example.com");
template.Execute<object>("hotels/{hotel}/bookings/{booking}", null, null, "42", "21");
}
//[Test]
//public void errorHandling() {
// Expect.Call(requestFactory.createRequest(new URI("http://example.com"), HttpMethod.GET)).andReturn(request);
// Expect.Call(request.execute()).andReturn(response);
// Expect.Call(errorHandler.hasError(response)).andReturn(true);
// Expect.Call(response.getStatusCode()).andReturn(HttpStatus.INTERNAL_SERVER_ERROR);
// Expect.Call(response.getStatusText()).andReturn("Internal Server Error");
// errorHandler.handleError(response);
// expectLastCall().andThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
// response.close();
// mocks.ReplayAll();
// try {
// template.execute("http://example.com", HttpMethod.GET, null, null);
// fail("HttpServerErrorException expected");
// }
// catch (HttpServerErrorException ex) {
// // expected
// }
// mocks.ReplayAll();
//}
[Test]
public void GetForObject()
{
Expect.Call<bool>(converter.CanRead(typeof(string), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "GET");
Expect.Call(request.Accept = "text/plain");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Expect.Call<bool>(converter.CanRead(typeof(string), textPlain)).Return(true);
String expected = "Hello World";
Expect.Call<string>(converter.Read<string>(response)).Return(expected);
mocks.ReplayAll();
string result = template.GetForObject<string>("http://example.com");
Assert.AreEqual(expected, result, "Invalid GET result");
}
[Test]
[ExpectedException(typeof(RestClientException),
ExpectedMessage = "Could not extract response: no suitable HttpMessageConverter found for response type [System.String] and content type [bar/baz]")]
public void GetUnsupportedMediaType()
{
Expect.Call<bool>(converter.CanRead(typeof(string), null)).Return(true);
MediaType textPlain = new MediaType("foo", "bar");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com/resource"))).Return(request);
Expect.Call(request.Method = "GET");
Expect.Call(request.Accept = "foo/bar");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
MediaType contentType = new MediaType("bar", "baz");
responseHeaders[HttpResponseHeader.ContentType] = contentType.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Expect.Call<bool>(converter.CanRead(typeof(string), contentType)).Return(false);
mocks.ReplayAll();
template.GetForObject<string>("http://example.com/{p}", "resource");
}
[Test]
public void GetForMessage()
{
Expect.Call<bool>(converter.CanRead(typeof(string), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "GET");
Expect.Call(request.Accept = "text/plain");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Expect.Call<bool>(converter.CanRead(typeof(string), textPlain)).Return(true);
String expected = "Hello World";
Expect.Call<string>(converter.Read<string>(response)).Return(expected);
Expect.Call<HttpStatusCode>(response.StatusCode).Return(HttpStatusCode.OK);
Expect.Call<string>(response.StatusDescription).Return("OK");
mocks.ReplayAll();
HttpResponseMessage<String> result = template.GetForMessage<string>("http://example.com");
Assert.AreEqual(expected, result.Body, "Invalid GET result");
Assert.AreEqual(textPlain.ToString(), result.Headers[HttpResponseHeader.ContentType], "Invalid Content-Type header");
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
Assert.AreEqual("OK", result.StatusDescription, "Invalid status description");
mocks.ReplayAll();
}
[Test]
public void HeadForHeaders()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "HEAD");
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
mocks.ReplayAll();
WebHeaderCollection result = template.HeadForHeaders("http://example.com");
Assert.AreSame(responseHeaders, result, "Invalid headers returned");
}
[Test]
public void PostForLocation()
{
string helloWorld = "Hello World";
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call<bool>(converter.CanWrite(typeof(string), null)).Return(true);
converter.Write(helloWorld, null, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
Uri expected = new Uri("http://example.com/hotels");
responseHeaders[HttpResponseHeader.Location] = expected.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
mocks.ReplayAll();
Uri result = template.PostForLocation("http://example.com", helloWorld);
Assert.AreEqual(expected, result, "Invalid POST result");
}
[Test]
public void PostForLocationMessageContentType()
{
string helloWorld = "Hello World";
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
MediaType contentType = new MediaType("text", "plain");
Expect.Call<bool>(converter.CanWrite(typeof(string), contentType)).Return(true);
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
converter.Write(helloWorld, contentType, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
Uri expected = new Uri("http://example.com/hotels");
responseHeaders[HttpResponseHeader.Location] = expected.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
mocks.ReplayAll();
WebHeaderCollection requestMessageHeaders = new WebHeaderCollection();
requestMessageHeaders[HttpRequestHeader.ContentType] = contentType.ToString();
HttpRequestMessage requestMessage = new HttpRequestMessage(helloWorld, requestMessageHeaders);
Uri result = template.PostForLocation("http://example.com", requestMessage);
Assert.AreEqual(expected, result, "Invalid POST result");
}
[Test]
public void PostForLocationEntityCustomHeader()
{
string helloWorld = "Hello World";
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call<bool>(converter.CanWrite(typeof(string), null)).Return(true);
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
converter.Write(helloWorld, null, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
Uri expected = new Uri("http://example.com/hotels");
responseHeaders[HttpResponseHeader.Location] = expected.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
mocks.ReplayAll();
WebHeaderCollection requestMessageHeaders = new WebHeaderCollection();
requestMessageHeaders.Add("MyHeader", "MyValue");
HttpRequestMessage requestMessage = new HttpRequestMessage(helloWorld, requestMessageHeaders);
Uri result = template.PostForLocation("http://example.com", requestMessage);
Assert.AreEqual(expected, result, "Invalid POST result");
Assert.AreEqual("MyValue", requestHeaders.Get("MyHeader"), "No custom header set");
}
[Test]
public void PostForLocationNoLocation()
{
string helloWorld = "Hello World";
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call<bool>(converter.CanWrite(typeof(string), null)).Return(true);
converter.Write(helloWorld, null, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
mocks.ReplayAll();
Uri result = template.PostForLocation("http://example.com", helloWorld);
Assert.IsNull(result, "Invalid POST result");
mocks.ReplayAll();
}
[Test]
public void PostForLocationNull()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
mocks.ReplayAll();
template.PostForLocation("http://example.com", null);
Assert.IsNull(requestHeaders[HttpRequestHeader.ContentLength], "Invalid content length");
}
[Test]
public void PostForObject()
{
Expect.Call<bool>(converter.CanRead(typeof(Version), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call(request.Accept = "text/plain");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
string helloWorld = "Hello World";
Expect.Call<bool>(converter.CanWrite(typeof(string), null)).Return(true);
converter.Write(helloWorld, null, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Version expected = new Version(1, 0);
Expect.Call<bool>(converter.CanRead(typeof(Version), textPlain)).Return(true);
Expect.Call<Version>(converter.Read<Version>(response)).Return(expected);
mocks.ReplayAll();
Version result = template.PostForObject<Version>("http://example.com", helloWorld);
Assert.AreEqual(expected, result, "Invalid POST result");
}
[Test]
public void PostForEntity()
{
Expect.Call<bool>(converter.CanRead(typeof(Version), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call(request.Accept = "text/plain");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
string helloWorld = "Hello World";
Expect.Call<bool>(converter.CanWrite(typeof(string), null)).Return(true);
converter.Write(helloWorld, null, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Version expected = new Version(1, 0);
Expect.Call<bool>(converter.CanRead(typeof(Version), textPlain)).Return(true);
Expect.Call<Version>(converter.Read<Version>(response)).Return(expected);
Expect.Call<HttpStatusCode>(response.StatusCode).Return(HttpStatusCode.OK);
Expect.Call<string>(response.StatusDescription).Return("OK");
mocks.ReplayAll();
HttpResponseMessage<Version> result = template.PostForMessage<Version>("http://example.com", helloWorld);
Assert.AreEqual(expected, result.Body, "Invalid POST result");
Assert.AreEqual(textPlain, MediaType.ParseMediaType(result.Headers[HttpResponseHeader.ContentType]), "Invalid Content-Type");
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
Assert.AreEqual("OK", result.StatusDescription, "Invalid status description");
}
[Test]
public void PostForObjectNull()
{
Expect.Call<bool>(converter.CanRead(typeof(Version), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call(request.Accept = "text/plain");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Expect.Call<bool>(converter.CanRead(typeof(Version), textPlain)).Return(true);
Expect.Call<Version>(converter.Read<Version>(response)).Return(null);
mocks.ReplayAll();
Version result = template.PostForObject<Version>("http://example.com", null);
Assert.IsNull(result, "Invalid POST result");
Assert.IsNull(requestHeaders[HttpRequestHeader.ContentLength], "Invalid content length");
}
[Test]
public void PostForEntityNull()
{
Expect.Call<bool>(converter.CanRead(typeof(Version), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call(request.Accept = "text/plain");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Expect.Call<bool>(converter.CanRead(typeof(Version), textPlain)).Return(true);
Expect.Call<Version>(converter.Read<Version>(response)).Return(null);
Expect.Call<HttpStatusCode>(response.StatusCode).Return(HttpStatusCode.OK);
Expect.Call<string>(response.StatusDescription).Return("OK");
mocks.ReplayAll();
HttpResponseMessage<Version> result = template.PostForMessage<Version>("http://example.com", null);
Assert.IsNull(result.Body, "Invalid POST result");
Assert.AreEqual(textPlain, MediaType.ParseMediaType(result.Headers[HttpResponseHeader.ContentType]), "Invalid Content-Type");
Assert.IsNull(requestHeaders[HttpRequestHeader.ContentLength], "Invalid content length");
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
Assert.AreEqual("OK", result.StatusDescription, "Invalid status description");
}
[Test]
public void Put()
{
Expect.Call<bool>(converter.CanWrite(typeof(string), null)).Return(true);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "PUT");
string helloWorld = "Hello World";
converter.Write(helloWorld, null, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
mocks.ReplayAll();
template.Put("http://example.com", helloWorld);
}
[Test]
public void PutNull()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "PUT");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
mocks.ReplayAll();
template.Put("http://example.com", null);
Assert.IsNull(requestHeaders[HttpRequestHeader.ContentLength], "Invalid content length");
}
[Test]
public void Delete()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "DELETE");
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
mocks.ReplayAll();
template.Delete("http://example.com");
}
[Test]
public void OptionsForAllow()
{
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "OPTIONS");
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.Allow] = "GET,POST";
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
mocks.ReplayAll();
IList<HttpMethod> result = template.OptionsForAllow("http://example.com");
Assert.AreEqual(2, result.Count, "Invalid OPTIONS result");
Assert.IsTrue(result.Contains(HttpMethod.GET), "Invalid OPTIONS result");
Assert.IsTrue(result.Contains(HttpMethod.POST), "Invalid OPTIONS result");
}
//[Test]
//public void ioException() {
// Expect.Call(converter.canRead(String.class, null)).andReturn(true);
// MediaType mediaType = new MediaType("foo", "bar");
// Expect.Call(converter.getSupportedMediaTypes()).andReturn(Collections.singletonList(mediaType));
// Expect.Call(requestFactory.createRequest(new URI("http://example.com/resource"), HttpMethod.GET)).andReturn(request);
// Expect.Call(request.getHeaders()).andReturn(new HttpHeaders());
// Expect.Call(request.execute()).andThrow(new IOException());
// mocks.ReplayAll();
// try {
// template.getForObject("http://example.com/resource", String.class);
// fail("RestClientException expected");
// }
// catch (ResourceAccessException ex) {
// // expected
// }
// mocks.ReplayAll();
//}
[Test]
public void Exchange()
{
Expect.Call<bool>(converter.CanRead(typeof(Version), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
IList<MediaType> mediaTypes = new List<MediaType>(1);
mediaTypes.Add(textPlain);
Expect.Call<IList<MediaType>>(converter.SupportedMediaTypes).Return(mediaTypes);
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
Expect.Call(request.Method = "POST");
Expect.Call(request.Accept = "text/plain");
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
string helloWorld = "Hello World";
Expect.Call<bool>(converter.CanWrite(typeof(string), null)).Return(true);
converter.Write(helloWorld, null, request);
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
Version expected = new Version(1, 0);
Expect.Call<bool>(converter.CanRead(typeof(Version), textPlain)).Return(true);
Expect.Call<Version>(converter.Read<Version>(response)).Return(expected);
Expect.Call<HttpStatusCode>(response.StatusCode).Return(HttpStatusCode.OK);
Expect.Call<string>(response.StatusDescription).Return("OK");
mocks.ReplayAll();
WebHeaderCollection requestMessageHeaders = new WebHeaderCollection();
requestMessageHeaders.Add("MyHeader", "MyValue");
HttpRequestMessage requestMessage = new HttpRequestMessage(helloWorld, requestMessageHeaders, HttpMethod.POST);
HttpResponseMessage<Version> result = template.Exchange<Version>("http://example.com", requestMessage);
Assert.AreEqual(expected, result.Body, "Invalid POST result");
Assert.AreEqual(textPlain, MediaType.ParseMediaType(result.Headers[HttpResponseHeader.ContentType]), "Invalid Content-Type");
Assert.AreEqual("MyValue", requestHeaders.Get("MyHeader"), "No custom header set");
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
Assert.AreEqual("OK", result.StatusDescription, "Invalid status description");
}
#region Utility methods
private void ExpectGetResponse()
{
Expect.Call<WebResponse>(request.GetResponse()).Return(response);
#region Instrumentation
if (LOG.IsDebugEnabled)
{
Expect.Call<HttpStatusCode>(response.StatusCode).Return(HttpStatusCode.OK);
Expect.Call<string>(response.StatusDescription).Return("OK");
}
#endregion
Expect.Call(response.Close);
Expect.Call<bool>(request.HaveResponse).Return(true);
}
#endregion
}
}

View File

@@ -0,0 +1,125 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F04CEE18-3897-A399-46BF-459437475B21}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>Spring.Http.Tests</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>Spring</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2008\Spring.Http.Tests\Debug\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;DEBUG;NET_2_0;NET_3_0;NET_3_5</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2008\Spring.Http.Tests\Release\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;NET_2_0;NET_3_0;NET_3_5</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=1.2.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<Name>nunit.framework</Name>
<HintPath>..\..\..\lib\Net\2.0\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="Rhino.Mocks, Version=3.4.0.0, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\Net\2.0\Rhino.Mocks.dll</HintPath>
</Reference>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Http\Converters\ByteArrayHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\StringHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\DataContractHttpMessageConverterIntegrationTests.cs" />
<Compile Include="Http\Converters\Xml\DataContractHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\XmlDocumentHttpMessageConverterTests.cs" />
<Compile Include="Http\MediaTypeTests.cs" />
<Compile Include="Http\Rest\RestTemplateIntegrationTests.cs" />
<Compile Include="Util\UriTemplateTests.cs" />
<Compile Include="Http\Rest\RestTemplateTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spring\Spring.Core\Spring.Core.2008.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2008</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Spring\Spring.Http\Spring.Http.2008.csproj">
<Project>{FAC04F79-4B1F-1D13-B30F-00EE04EC0FBC}</Project>
<Name>Spring.Http.2008</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Spring.Http.Tests.dll.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>echo "Copying .xml files for tests"
xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2008\Spring.Http.Tests\$(ConfigurationName)\ /y /s /q /d
xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2008\Spring.Http.Tests\$(ConfigurationName)\ /y /s /q</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{4594CEE7-3897-A3BF-9946-5B4374F01821}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>Spring.Http.Tests</AssemblyName>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>Spring</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation />
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2010\Spring.Http.Tests\Debug\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;DEBUG;NET_2_0;NET_3_0;NET_3_5;NET_4_0</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>..\..\..\build\VS.Net.2010\Spring.Http.Tests\Release\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE;NET_2_0;NET_3_0;NET_3_5</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="Common.Logging, Version=1.2.0.0, Culture=neutral, PublicKeyToken=af08829b84f0328e">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\Net\2.0\Common.Logging.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<Name>nunit.framework</Name>
<HintPath>..\..\..\lib\Net\2.0\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="Rhino.Mocks, Version=3.4.0.0, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\lib\Net\2.0\Rhino.Mocks.dll</HintPath>
</Reference>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.ServiceModel.Web" />
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Http\Converters\ByteArrayHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\StringHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\DataContractHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\DataContractHttpMessageConverterIntegrationTests.cs" />
<Compile Include="Http\Converters\Xml\XmlDocumentHttpMessageConverterTests.cs" />
<Compile Include="Http\MediaTypeTests.cs" />
<Compile Include="Http\Rest\RestTemplateIntegrationTests.cs" />
<Compile Include="Http\Rest\RestTemplateTests.cs" />
<Compile Include="Util\UriTemplateTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spring\Spring.Core\Spring.Core.2010.csproj">
<Project>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</Project>
<Name>Spring.Core.2010</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Spring\Spring.Http\Spring.Http.2010.csproj">
<Project>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</Project>
<Name>Spring.Http.2010</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Spring.Http.Tests.dll.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>echo "Copying .xml files for tests"
xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2010\Spring.Http.Tests\$(ConfigurationName)\ /y /s /q /d
xcopy "$(ProjectDir)$(TargetFileName).config" ..\..\..\..\build\VS.Net.2010\Spring.Http.Tests\$(ConfigurationName)\ /y /s /q</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
</configSections>
<common>
<logging>
<factoryAdapter type="Common.Logging.Simple.TraceLoggerFactoryAdapter, Common.Logging">
<arg key="level" value="ALL" />
</factoryAdapter>
</logging>
</common>
</configuration>

View File

@@ -0,0 +1,227 @@
#region License
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Spring.Util
{
/// <summary>
/// Unit tests for the UriTemplate class.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Juergen Hoeller</author>
/// <author>Bruno Baia (.NET)</author>
[TestFixture]
public class UriTemplateTests
{
[Test]
public void GetVariableNames()
{
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
string[] variableNames = template.VariableNames;
Assert.AreEqual(new string[] { "hotel", "booking" }, variableNames, "Invalid variable names");
}
[Test]
public void ExpandVarArgs()
{
// absolute
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
Uri result = template.Expand("1", "42");
Assert.AreEqual(new Uri("http://example.com/hotels/1/bookings/42"), result, "Invalid expanded template");
// relative
template = new UriTemplate("/hotels/{hotel}/bookings/{booking}");
result = template.Expand("1", "42");
Assert.AreEqual(new Uri("/hotels/1/bookings/42", UriKind.Relative), result, "Invalid expanded template");
}
[Test]
public void MultipleExpandVarArgs()
{
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
Uri result = template.Expand("2", "21");
result = template.Expand("1", "42");
Assert.AreEqual(new Uri("http://example.com/hotels/1/bookings/42"), result, "Invalid expanded template");
}
[Test]
[ExpectedException(
typeof(ArgumentException),
ExpectedMessage = "Invalid amount of variables values in 'http://example.com/hotels/{hotel}/bookings/{booking}': expected 2; got 3")]
public void ExpandVarArgsInvalidAmountVariables()
{
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
template.Expand("1", "42", "100");
}
[Test]
public void ExpandVarArgsDuplicateVariables()
{
UriTemplate template = new UriTemplate("http://example.com/order/{c}/{c}/{c}");
Assert.AreEqual(new string[] { "c" }, template.VariableNames, "Invalid variable names");
Uri result = template.Expand("cheeseburger");
Assert.AreEqual(new Uri("http://example.com/order/cheeseburger/cheeseburger/cheeseburger"), result, "Invalid expanded template");
}
[Test]
public void ExpandDictionary()
{
IDictionary<string, string> uriVariables = new Dictionary<String, String>(2);
uriVariables.Add("booking", "42");
uriVariables.Add("hotel", "1");
// absolute
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
Uri result = template.Expand(uriVariables);
Assert.AreEqual(new Uri("http://example.com/hotels/1/bookings/42"), result, "Invalid expanded template");
// relative
template = new UriTemplate("hotels/{hotel}/bookings/{booking}");
result = template.Expand(uriVariables);
Assert.AreEqual(new Uri("hotels/1/bookings/42", UriKind.Relative), result, "Invalid expanded template");
}
[Test]
public void MultipleExpandDictionary()
{
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
IDictionary<string, string> uriVariables = new Dictionary<String, String>(2);
uriVariables.Add("booking", "21");
uriVariables.Add("hotel", "2");
Uri result = template.Expand(uriVariables);
uriVariables = new Dictionary<String, String>(2);
uriVariables.Add("booking", "42");
uriVariables.Add("hotel", "1");
result = template.Expand(uriVariables);
Assert.AreEqual(new Uri("http://example.com/hotels/1/bookings/42"), result, "Invalid expanded template");
}
[Test]
[ExpectedException(
typeof(ArgumentException),
ExpectedMessage = "Invalid amount of variables values in 'http://example.com/hotels/{hotel}/bookings/{booking}': expected 2; got 1")]
public void ExpandDictionaryInvalidAmountVariables()
{
IDictionary<string, string> uriVariables = new Dictionary<String, String>(2);
uriVariables.Add("hotel", "1");
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
template.Expand(uriVariables);
}
[Test]
[ExpectedException(
typeof(ArgumentException),
ExpectedMessage = "'uriVariables' dictionary has no value for 'hotel'")]
public void ExpandDictionaryUnboundVariables()
{
IDictionary<string, string> uriVariables = new Dictionary<String, String>(2);
uriVariables.Add("booking", "42");
uriVariables.Add("bar", "1");
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
template.Expand(uriVariables);
}
[Test]
public void ExpandEncoded()
{
UriTemplate template = new UriTemplate("http://example.com/hotel list/{hotel}");
Uri result = template.Expand("Z\u00fcrich");
Assert.AreEqual(new Uri("http://example.com/hotel%20list/Z%C3%BCrich"), result, "Invalid expanded template");
}
[Test]
public void Matches()
{
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}/");
Assert.IsTrue(template.Matches("http://example.com/hotels/1/bookings/42/"), "UriTemplate does not match");
Assert.IsFalse(template.Matches("hhhhttp://example.com/hotels/1/bookings/42/"), "UriTemplate matches");
Assert.IsFalse(template.Matches("http://example.com/hotels/1/bookings/42/blabla"), "UriTemplate matches");
Assert.IsFalse(template.Matches("http://example.com/hotels/bookings/"), "UriTemplate matches");
Assert.IsFalse(template.Matches(""), "UriTemplate matches");
Assert.IsFalse(template.Matches(null), "UriTemplate matches");
}
[Test]
public void Match()
{
UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
IDictionary<string, string> result = template.Match("http://example.com/hotels/1/bookings/42");
Assert.AreEqual(2, result.Count);
Assert.AreEqual("1", result["hotel"]);
Assert.AreEqual("42", result["booking"]);
result = template.Match("http://example.com/hotels/1/bookings");
Assert.AreEqual(0, result.Count);
}
[Test]
public void matchDuplicate()
{
UriTemplate template = new UriTemplate("/order/{c}/{c}/{c}");
IDictionary<string, string> result = template.Match("/order/cheeseburger/cheeseburger/cheeseburger");
Assert.AreEqual(1, result.Count);
Assert.AreEqual("cheeseburger", result["c"]);
}
[Test]
public void MatchMultipleInOneSegment()
{
UriTemplate template = new UriTemplate("/{foo}-{bar}");
IDictionary<string, string> result = template.Match("/12-34");
Assert.AreEqual(2, result.Count);
Assert.AreEqual("12", result["foo"]);
Assert.AreEqual("34", result["bar"]);
}
[Test]
public void MatchesQueryVariables()
{
UriTemplate template = new UriTemplate("/search?q={query}");
Assert.IsTrue(template.Matches("/search?q=foo"));
}
[Test]
public void MatchesFragments()
{
UriTemplate template = new UriTemplate("/search#{fragment}");
Assert.IsTrue(template.Matches("/search#foo"));
template = new UriTemplate("/search?query={query}#{fragment}");
Assert.IsTrue(template.Matches("/search?query=foo#bar"));
}
[Test]
public void ExpandWithAtSign()
{
UriTemplate template = new UriTemplate("http://localhost/query={query}");
Uri uri = template.Expand("foo@bar");
Assert.AreEqual("http://localhost/query=foo@bar", uri.ToString());
}
}
}