REST client API: Dev (SPRNET-1345)

This commit is contained in:
bbaia
2010-08-03 15:03:38 +00:00
parent dfd305a58d
commit 9998999a95
60 changed files with 4337 additions and 1714 deletions

View File

@@ -1,7 +1,5 @@
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}"
@@ -14,10 +12,6 @@ Global
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

View File

@@ -1,7 +1,5 @@
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}"
@@ -14,10 +12,6 @@ Global
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

View File

@@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Xml.Linq;
using Spring.Http.Rest;
@@ -11,9 +13,19 @@ namespace Spring.RestQuickStart
try
{
RestTemplate rt = new RestTemplate("http://twitter.com");
string result = rt.GetForObject<string>("/statuses/user_timeline.xml?id={id}", "lancearmstrong");
Console.WriteLine(result);
//string result = rt.GetForObject<string>("/statuses/user_timeline.xml?id={id}&count={2}", "SpringForNet", "10");
//Console.WriteLine(result);
XElement result = rt.GetForObject<XElement>("/statuses/user_timeline.xml?id={id}&count={2}", "SpringForNet", "10");
var tweets = from el in result.Elements("status")
select el.Element("text").Value;
foreach (string tweet in tweets)
{
Console.WriteLine(String.Format("* {0}", tweet));
Console.WriteLine();
}
}
catch (Exception ex)
{

View File

@@ -32,6 +32,10 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
@@ -40,10 +44,6 @@
<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>

View File

@@ -52,6 +52,8 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
@@ -60,10 +62,6 @@
<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>

View File

@@ -19,6 +19,7 @@
#endregion
using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
@@ -26,17 +27,17 @@ 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
*/
/// <summary>
/// Base class for most <see cref="IHttpMessageConverter"/> implementations.
/// </summary>
/// <remarks>
/// This base class adds support for setting supported <see cref="MediaType"/>s, through the
/// <see cref="P:SupportedMediaTypes"/> property.
/// It also adds support for 'Content-Type' when writing to the HTTP request.
/// </remarks>
/// <author>Arjen Poutsma</author>
/// <author>Juergen Hoeller</author>
/// <author>Bruno Baia (.NET)</author>
public abstract class AbstractHttpMessageConverter : IHttpMessageConverter
{
#region Logging
@@ -47,9 +48,9 @@ namespace Spring.Http.Converters
private IList<MediaType> _supportedMediaTypes = new List<MediaType>();
/**
* Set the list of {@link MediaType} objects supported by this converter.
*/
/// <summary>
/// Gets or sets the list of <see cref="MediaType"/> objects supported by this converter.
/// </summary>
public IList<MediaType> SupportedMediaTypes
{
get { return _supportedMediaTypes; }
@@ -58,18 +59,19 @@ namespace Spring.Http.Converters
#region Constructor(s)
/**
* Construct an {@code AbstractHttpMessageConverter} with no supported media types.
* @see #setSupportedMediaTypes
*/
/// <summary>
/// Creates a new instance of the <see cref="AbstractHttpMessageConverter"/>
/// with no supported media types.
/// </summary>
protected AbstractHttpMessageConverter()
{
}
/**
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
* @param supportedMediaTypes the supported media types
*/
/// <summary>
/// Creates a new instance of the <see cref="AbstractHttpMessageConverter"/>
/// with multiple supported media type.
/// </summary>
/// <param name="supportedMediaTypes">The supported media types.</param>
protected AbstractHttpMessageConverter(params MediaType[] supportedMediaTypes)
{
this._supportedMediaTypes = new List<MediaType>(supportedMediaTypes);
@@ -79,47 +81,80 @@ namespace Spring.Http.Converters
#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)
/// <summary>
/// Indicates whether the given class can be read by this converter.
/// </summary>
/// <remarks>
/// This implementation checks if the given class is <see cref="M:Supports(Type)">supported</see>,
/// and if the <see cref="P:SupportedMediaTypes">supported media types</see> <see cref="M:MediaType.Includes(MediaType)">include</see>
/// the given media type.
/// </remarks>
/// <param name="type">The class to test for readability</param>
/// <param name="mediaType">
/// The media type to read, can be null if not specified. Typically the value of a 'Content-Type' header.
/// </param>
/// <returns><see langword="true"/> if readable; otherwise <see langword="false"/></returns>
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.
*/
/// <summary>
/// Indicates whether the given class can be written by this converter.
/// </summary>
/// <remarks>
/// This implementation checks if the given class is <see cref="M:Supports(Type)">supported</see>,
/// and if the <see cref="P:SupportedMediaTypes">supported media types</see> <see cref="M:MediaType.Includes(MediaType)">include</see>
/// the given media type.
/// </remarks>
/// <param name="type">The class to test for writability</param>
/// <param name="mediaType">
/// The media type to write, can be null if not specified. Typically the value of an 'Accept' header.
/// </param>
/// <returns><see langword="true"/> if writable; otherwise <see langword="false"/></returns>
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.
*/
/// <summary>
/// Read an object of the given type form the given HTTP response, and returns it.
/// </summary>
/// <remarks>
/// This implementation simple delegates to <see cre="ReadInternal"/> method.
/// Future implementations might add some default behavior, however.
/// </remarks>
/// <typeparam name="T">
/// The type of object to return. This type must have previously been passed to the
/// <see cref="M:CanRead"/> method of this interface, which must have returned <see langword="true"/>.
/// </typeparam>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
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}.
*/
/// <summary>
/// Write an given object to the given HTTP request.
/// </summary>
/// <remarks>
/// This implementation delegates to <see cref="M:GetDefaultContentType"/> method if a content
/// type was not provided, and calls <see cref="M:WriteInternal"/>.
/// </remarks>
/// <param name="content">
/// The object to write to the HTTP request. The type of this object must have previously been
/// passed to the <see cref="M:CanWrite"/> method of this interface, which must have returned <see langword="true"/>.
/// </param>
/// <param name="mediaType">
/// The content type to use when writing. May be null to indicate that the default content type of the converter must be used.
/// If not null, this media type must have previously been passed to the <see cref="M:CanWrite"/> method of this interface,
/// which must have returned <see langword="true"/>.
/// </param>
/// <param name="request">The HTTP request to write to.</param>
public void Write(object content, MediaType mediaType, HttpWebRequest request)
{
if (!StringUtils.HasText(request.Headers[HttpRequestHeader.ContentType]))
if (!StringUtils.HasText(request.ContentType))
{
if (mediaType == null || mediaType.IsWildcardType || mediaType.IsWildcardSubtype)
{
@@ -135,14 +170,16 @@ namespace Spring.Http.Converters
#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)
/// <summary>
/// Returns true if any of the <see cref="P:SupportedMediaTypes">supported media types</see> include the given media type.
/// </summary>
/// <param name="mediaType">
/// The media type to read, can be null if not specified. Typically the value of a 'Content-Type' header.
/// </param>
/// <returns>
/// <see langword="true"/> if the supported media types include the media type, or if the media type is null.
/// </returns>
protected bool CanRead(MediaType mediaType)
{
if (mediaType == null)
{
@@ -158,13 +195,15 @@ namespace Spring.Http.Converters
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}
*/
/// <summary>
/// Returns true if the given media type includes any of the <see cref="P:SupportedMediaTypes">supported media types</see>.
/// </summary>
/// <param name="mediaType">
/// The media type to write, can be {@code null} if not specified. Typically the value of an 'Accept' header.
/// </param>
/// <returns>
/// <see langword="true"/> if the supported media types are compatible with the media type, or if the media type is null.
/// </returns>
protected bool CanWrite(MediaType mediaType)
{
if (mediaType == null || mediaType.Equals(MediaType.ALL))
@@ -181,44 +220,74 @@ namespace Spring.Http.Converters
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
*/
/// <summary>
/// Returns the default content type for the given type.
/// Called when <see cref="M:Write"/> is invoked without a specified content type parameter.
/// </summary>
/// <remarks>
/// By default, this returns the first element of the <see cref="P:SupportedMediaTypes"/> property, if any.
/// </remarks>
/// <param name="type">The type to return the content type for.</param>
/// <returns>The <see cref="MediaType">content type</see>, or null if not known.</returns>
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
*/
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
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
*/
/// <summary>
/// Abstract template method that reads the actualy object. Invoked from <see cref="M:Read"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
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
*/
/// <summary>
/// Abstract template method that writes the actual body. Invoked from <see cref="M:Write"/>.
/// </summary>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected abstract void WriteInternal(object content, HttpWebRequest request);
#region Inner class definitions
// TODO : Move this class
internal class IgnoreCloseMemoryStream : MemoryStream
{
public IgnoreCloseMemoryStream()
: base()
{
}
public override void Close()
{
}
public void CopyToAndClose(Stream dest)
{
this.Position = 0;
int bufferSize = 65536;
byte[] buffer = new byte[bufferSize];
int bytesCount;
while ((bytesCount = this.Read(buffer, 0, buffer.Length)) > 0)
{
dest.Write(buffer, 0, bytesCount);
}
dest.Flush();
base.Close();
}
}
#endregion
}
}

View File

@@ -25,29 +25,43 @@ 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
*/
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write byte arrays.
/// </summary>
/// <remarks>
/// By default, this converter supports all media types '*/*', and writes with a 'Content-Type'
/// of 'application/octet-stream'.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class ByteArrayHttpMessageConverter : AbstractHttpMessageConverter
{
/** Creates a new instance of the {@code ByteArrayHttpMessageConverter}. */
/// <summary>
/// Creates a new instance of the <see cref="ByteArrayHttpMessageConverter"/>
/// with 'application/octet-stream', and '*/*' media types.
/// </summary>
public ByteArrayHttpMessageConverter() :
base(new MediaType("application", "octet-stream"), MediaType.ALL)
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
protected override bool Supports(Type type)
{
return type.Equals(typeof(byte[]));
}
/// <summary>
/// Abstract template method that reads the actualy object. Invoked from <see cref="M:Read"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadInternal<T>(HttpWebResponse response)
{
// Get the response stream
@@ -57,6 +71,11 @@ namespace Spring.Http.Converters
}
}
/// <summary>
/// Abstract template method that writes the actual body. Invoked from <see cref="M:Write"/>.
/// </summary>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteInternal(object content, HttpWebRequest request)
{
// Create a byte array of the data we want to send
@@ -65,7 +84,7 @@ namespace Spring.Http.Converters
// Set the content length in the request headers
request.ContentLength = byteData.Length;
// Write data
// Write to the request
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);

View File

@@ -28,27 +28,58 @@ using Spring.Http.Converters.Xml;
namespace Spring.Http.Converters.Feed
{
/// <summary>
/// Base class for Atom and RSS Feed message converters
/// using the <see cref="System.ServiceModel.Syndication.SyndicationFeed"/> class.
/// </summary>
/// <author>Bruno Baia</author>
public abstract class AbstractFeedHttpMessageConverter : AbstractXmlHttpMessageConverter
{
/**
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
* @param supportedMediaTypes the supported media types
*/
{
/// <summary>
/// Creates a new instance of the <see cref="AbstractXmlHttpMessageConverter"/>
/// with multiple supported media type.
/// </summary>
/// <param name="supportedMediaTypes">The supported media types.</param>
protected AbstractFeedHttpMessageConverter(params MediaType[] supportedMediaTypes) :
base(supportedMediaTypes)
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
protected override bool Supports(Type type)
{
return type.Equals(typeof(SyndicationFeed));
return type.Equals(typeof(SyndicationFeed)) || type.Equals(typeof(SyndicationItem));
}
/// <summary>
/// Abstract template method that reads the actualy object using a <see cref="XmlReader"/>. Invoked from <see cref="M:ReadInternal"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="xmlReader">The XmlReader to use.</param>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
return SyndicationFeed.Load(xmlReader) as T;
if (typeof(SyndicationFeed).Equals(typeof(T)))
{
return SyndicationFeed.Load(xmlReader) as T;
}
if (typeof(SyndicationItem).Equals(typeof(T)))
{
return SyndicationItem.Load(xmlReader) as T;
}
return null;
}
/// <summary>
/// Returns the default <see cref="XmlReaderSettings">XmlReader settings</see>
/// used by this converter to read from the HTTP response.
/// </summary>
/// <returns>The XmlReader settings.</returns>
protected override XmlReaderSettings GetDefaultXmlReaderSettings()
{
XmlReaderSettings settings = new XmlReaderSettings();

View File

@@ -26,17 +26,44 @@ using System.ServiceModel.Syndication;
namespace Spring.Http.Converters.Feed
{
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write Atom feeds
/// using the <see cref="System.ServiceModel.Syndication.SyndicationFeed"/> class.
/// </summary>
/// <remarks>
/// By default, this converter reads and writes the media type 'application/atom+xml' media type.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Bruno Baia</author>
public class Atom10FeedHttpMessageConverter : AbstractFeedHttpMessageConverter
{
/// <summary>
/// Creates a new instance of the <see cref="Atom10FeedHttpMessageConverter"/>
/// with 'application/atom+xml', 'application/xml' and 'text/xml' media types.
/// </summary>
public Atom10FeedHttpMessageConverter() :
base(new MediaType("application", "atom+xml"))
base(new MediaType("application", "atom+xml"), new MediaType("application", "xml"), new MediaType("text", "xml"))
{
}
/// <summary>
/// Abstract template method that writes the actual body using a <see cref="XmlWriter"/>. Invoked from <see cref="M:WriteInternal"/>.
/// </summary>
/// <param name="xmlWriter">The XmlWriter to use.</param>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
SyndicationFeed rssFeed = content as SyndicationFeed;
rssFeed.SaveAsAtom10(xmlWriter);
if (content is SyndicationFeed)
{
SyndicationFeed atomFeed = content as SyndicationFeed;
atomFeed.SaveAsAtom10(xmlWriter);
}
else if (content is SyndicationItem)
{
SyndicationItem atomItem = content as SyndicationItem;
atomItem.SaveAsAtom10(xmlWriter);
}
}
}
}

View File

@@ -0,0 +1,70 @@
#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
{
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write RSS feeds
/// using the <see cref="System.ServiceModel.Syndication.SyndicationFeed"/> class.
/// </summary>
/// <remarks>
/// By default, this converter reads and writes the media type 'application/rss+xml' media type.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Bruno Baia</author>
public class Rss20FeedHttpMessageConverter : AbstractFeedHttpMessageConverter
{
/// <summary>
/// Creates a new instance of the <see cref="Rss20FeedHttpMessageConverter"/>
/// with 'application/rss+xml', 'application/xml' and 'text/xml' media types.
/// </summary>
public Rss20FeedHttpMessageConverter() :
base(new MediaType("application", "rss+xml"), new MediaType("application", "xml"), new MediaType("text", "xml"))
{
}
/// <summary>
/// Abstract template method that writes the actual body using a <see cref="XmlWriter"/>. Invoked from <see cref="M:WriteInternal"/>.
/// </summary>
/// <param name="xmlWriter">The XmlWriter to use.</param>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
if (content is SyndicationFeed)
{
SyndicationFeed rssFeed = content as SyndicationFeed;
rssFeed.SaveAsRss20(xmlWriter);
}
else if (content is SyndicationItem)
{
SyndicationItem rssItem = content as SyndicationItem;
rssItem.SaveAsRss20(xmlWriter);
}
}
}
}
#endif

View File

@@ -1,43 +0,0 @@
#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

@@ -24,62 +24,65 @@ 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
*/
// TODO: HttpMessageNotReadableException & HttpMessageNotWritableException exceptions ?
/// <summary>
/// Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Juergen Hoeller</author>
/// <author>Bruno Baia (.NET)</author>
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
*/
/// <summary>
/// Indicates whether the given class can be read by this converter.
/// </summary>
/// <param name="type">The class to test for readability</param>
/// <param name="mediaType">
/// The media type to read, can be null if not specified. Typically the value of a 'Content-Type' header.
/// </param>
/// <returns><see langword="true"/> if readable; otherwise <see langword="false"/></returns>
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
*/
/// <summary>
/// Indicates whether the given class can be written by this converter.
/// </summary>
/// <param name="type">The class to test for writability</param>
/// <param name="mediaType">
/// The media type to write, can be null if not specified. Typically the value of an 'Accept' header.
/// </param>
/// <returns><see langword="true"/> if writable; otherwise <see langword="false"/></returns>
bool CanWrite(Type type, MediaType mediaType);
/**
* Return the list of {@link MediaType} objects supported by this converter.
* @return the list of supported media types
*/
/// <summary>
/// Gets the list of <see cref="MediaType"/> objects supported by this converter.
/// </summary>
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
*/
/// <summary>
/// Read an object of the given type form the given HTTP response, and returns it.
/// </summary>
/// <typeparam name="T">
/// The type of object to return. This type must have previously been passed to the
/// <see cref="M:CanRead"/> method of this interface, which must have returned <see langword="true"/>.
/// </typeparam>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
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
*/
/// <summary>
/// Write an given object to the given HTTP request.
/// </summary>
/// <param name="content">
/// The object to write to the HTTP request. The type of this object must have previously been
/// passed to the <see cref="M:CanWrite"/> method of this interface, which must have returned <see langword="true"/>.
/// </param>
/// <param name="mediaType">
/// The content type to use when writing. May be null to indicate that the default content type of the converter must be used.
/// If not null, this media type must have previously been passed to the <see cref="M:CanWrite"/> method of this interface,
/// which must have returned <see langword="true"/>.
/// </param>
/// <param name="request">The HTTP request to write to.</param>
void Write(object content, MediaType mediaType, HttpWebRequest request);
}
}

View File

@@ -31,20 +31,48 @@ using Spring.Util;
namespace Spring.Http.Converters.Json
{
// TODO : Support for known types, etc...
// TODO : Fix Write method
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write JSON.
/// </summary>
/// <remarks>
/// By default, this converter supports 'application/json' media type.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Bruno Baia</author>
public class JsonHttpMessageConverter : AbstractHttpMessageConverter
{
/// <summary>
/// Default encoding for JSON.
/// </summary>
public static readonly Encoding DEFAULT_CHARSET = Encoding.UTF8;
/// <summary>
/// Creates a new instance of the <see cref="JsonHttpMessageConverter"/>
/// with the media type 'application/json'.
/// </summary>
public JsonHttpMessageConverter() :
base(new MediaType("application", "json"))
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
protected override bool Supports(Type type)
{
return true;
}
/// <summary>
/// Abstract template method that reads the actualy object. Invoked from <see cref="M:Read"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadInternal<T>(HttpWebResponse response)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
@@ -54,12 +82,17 @@ namespace Spring.Http.Converters.Json
}
}
/// <summary>
/// Abstract template method that writes the actual body. Invoked from <see cref="M:Write"/>.
/// </summary>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteInternal(object content, HttpWebRequest request)
{
// Get the request encoding
MediaType mediaType = MediaType.ParseMediaType(request.Headers[HttpRequestHeader.ContentType]);
MediaType mediaType = MediaType.ParseMediaType(request.ContentType);
Encoding encoding;
if (mediaType == null || String.IsNullOrEmpty(mediaType.CharSet))
if (mediaType == null || !StringUtils.HasText(mediaType.CharSet))
{
encoding = DEFAULT_CHARSET;
}
@@ -70,18 +103,19 @@ namespace Spring.Http.Converters.Json
DataContractJsonSerializer serializer = new DataContractJsonSerializer(content.GetType());
// Write data
using (Stream postStream = request.GetRequestStream())
// Write to the request
using (IgnoreCloseMemoryStream requestStream = new IgnoreCloseMemoryStream())
{
using (XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(postStream, encoding, false))
using (XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(requestStream, encoding, false))
{
serializer.WriteObject(jsonWriter, content);
jsonWriter.Flush();
}
postStream.Flush();
// Set the content length in the request headers
request.ContentLength = postStream.Length;
request.ContentLength = requestStream.Length;
requestStream.CopyToAndClose(request.GetRequestStream());
}
}
}

View File

@@ -23,37 +23,57 @@ using System.IO;
using System.Net;
using System.Text;
using Spring.Util;
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
*/
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write strings.
/// </summary>
/// <remarks>
/// By default, this converter supports all media types '*/*', and writes with a 'Content-Type'
/// of 'text/plain'.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class StringHttpMessageConverter : AbstractHttpMessageConverter
{
/// <summary>
/// Default encoding for strings.
/// </summary>
public static readonly Encoding DEFAULT_CHARSET = Encoding.GetEncoding("ISO-8859-1");
/// <summary>
/// Creates a new instance of the <see cref="ByteArrayHttpMessageConverter"/>
/// with 'text/plain; charset=ISO-8859-1', and '*/*' media types.
/// </summary>
public StringHttpMessageConverter() :
base(new MediaType("text", "plain", "ISO-8859-1"), MediaType.ALL)
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
protected override bool Supports(Type type)
{
return type.Equals(typeof(string));
}
/// <summary>
/// Abstract template method that reads the actualy object. Invoked from <see cref="M:Read"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadInternal<T>(HttpWebResponse response)
{
// Get the response encoding
Encoding encoding;
if (String.IsNullOrEmpty(response.CharacterSet))
if (!StringUtils.HasText(response.CharacterSet))
{
encoding = DEFAULT_CHARSET;
}
@@ -69,12 +89,17 @@ namespace Spring.Http.Converters
}
}
/// <summary>
/// Abstract template method that writes the actual body. Invoked from <see cref="M:Write"/>.
/// </summary>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
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))
MediaType mediaType = MediaType.ParseMediaType(request.ContentType);
if (mediaType == null || !StringUtils.HasText(mediaType.CharSet))
{
encoding = DEFAULT_CHARSET;
}
@@ -89,7 +114,7 @@ namespace Spring.Http.Converters
// Set the content length in the request headers
request.ContentLength = byteData.Length;
// Write data
// Write to the request
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);

View File

@@ -18,20 +18,36 @@
#endregion
using System;
using System.IO;
using System.Xml;
using System.Net;
using System.Text;
using Spring.Util;
namespace Spring.Http.Converters.Xml
{
/// <summary>
/// Base class for <see cref="IHttpMessageConverter"/> that convert from/to XML.
/// </summary>
/// <remarks>
/// By default, subclasses of this converter support 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Bruno Baia</author>
public abstract class AbstractXmlHttpMessageConverter : AbstractHttpMessageConverter
{
/// <summary>
/// Default encoding for XML.
/// </summary>
public static readonly Encoding DEFAULT_CHARSET = Encoding.UTF8;
private XmlReaderSettings _xmlReaderSettings;
/// <summary>
/// Gets or sets the <see cref="XmlReaderSettings">XmlReader settings</see>
/// used by this converter to read from the HTTP response.
/// </summary>
public XmlReaderSettings XmlReaderSettings
{
get
@@ -45,15 +61,31 @@ namespace Spring.Http.Converters.Xml
set { _xmlReaderSettings = value; }
}
/**
* Construct an {@code AbstractHttpMessageConverter} with multiple supported media type.
* @param supportedMediaTypes the supported media types
*/
/// <summary>
/// Creates a new instance of the <see cref="AbstractHttpMessageConverter"/>
/// with multiple supported media type.
/// </summary>
/// <param name="supportedMediaTypes">The supported media types.</param>
protected AbstractXmlHttpMessageConverter(params MediaType[] supportedMediaTypes) :
base(supportedMediaTypes)
{
}
/// <summary>
/// Creates a new instance of the <see cref="AbstractHttpMessageConverter"/> that sets
/// the <see cref="P:SupportedMediaTypes"/> to 'text/xml' and 'application/xml', and 'application/*-xml'.
/// </summary>
protected AbstractXmlHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
{
}
/// <summary>
/// Abstract template method that reads the actualy object. Invoked from <see cref="M:Read"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadInternal<T>(HttpWebResponse response)
{
using (Stream stream = response.GetResponseStream())
@@ -65,12 +97,17 @@ namespace Spring.Http.Converters.Xml
}
}
/// <summary>
/// Abstract template method that writes the actual body. Invoked from <see cref="M:Write"/>.
/// </summary>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
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))
MediaType mediaType = MediaType.ParseMediaType(request.ContentType);
if (mediaType == null || !StringUtils.HasText(mediaType.CharSet))
{
encoding = DEFAULT_CHARSET;
}
@@ -79,20 +116,44 @@ namespace Spring.Http.Converters.Xml
encoding = Encoding.GetEncoding(mediaType.CharSet);
}
using (Stream postStream = request.GetRequestStream())
// Write to the request
using (IgnoreCloseMemoryStream requestStream = new IgnoreCloseMemoryStream())
{
using (XmlTextWriter xmlWriter = new XmlTextWriter(postStream, encoding))
using (XmlTextWriter xmlWriter = new XmlTextWriter(requestStream, encoding))
{
WriteXml(xmlWriter, content, request);
xmlWriter.Flush();
}
// TODO : Don't work
// Set the content length in the request headers
request.ContentLength = postStream.Length;
request.ContentLength = requestStream.Length;
requestStream.CopyToAndClose(request.GetRequestStream());
}
}
/// <summary>
/// Abstract template method that reads the actualy object using a <see cref="XmlReader"/>. Invoked from <see cref="M:ReadInternal"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="xmlReader">The XmlReader to use.</param>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected abstract T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response) where T : class;
/// <summary>
/// Abstract template method that writes the actual body using a <see cref="XmlWriter"/>. Invoked from <see cref="M:WriteInternal"/>.
/// </summary>
/// <param name="xmlWriter">The XmlWriter to use.</param>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected abstract void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request);
/// <summary>
/// Returns the default <see cref="XmlReaderSettings">XmlReader settings</see>
/// used by this converter to read from the HTTP response.
/// </summary>
/// <returns>The XmlReader settings.</returns>
protected virtual XmlReaderSettings GetDefaultXmlReaderSettings()
{
XmlReaderSettings settings = new XmlReaderSettings();
@@ -102,9 +163,5 @@ namespace Spring.Http.Converters.Xml
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

@@ -26,67 +26,72 @@ 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;
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write XML
/// using <see cref="DataContractSerializer"/>.
/// </summary>
/// <remarks>
/// <para>
/// By default, this converter supports 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </para>
/// <para>
/// This converter can read classes annotated with <see cref="DataContractAttribute"/> and <see cref="CollectionDataContractAttribute"/>, and write classes
/// annotated with with {@link XmlRootElement}, or subclasses thereof.
/// </para>
/// </remarks>
/// <author>Bruno Baia</author>
public class DataContractHttpMessageConverter : AbstractXmlHttpMessageConverter
{
/// <summary>
/// Creates a new instance of the <see cref="DataContractHttpMessageConverter"/>
/// with 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// </summary>
public DataContractHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
base()
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
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));
return (
Attribute.GetCustomAttributes(type, typeof(DataContractAttribute), true).Length > 0 ||
Attribute.GetCustomAttributes(type, typeof(CollectionDataContractAttribute), true).Length > 0
);
}
protected override T ReadInternal<T>(HttpWebResponse response)
/// <summary>
/// Abstract template method that reads the actualy object using a <see cref="XmlReader"/>. Invoked from <see cref="M:ReadInternal"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="xmlReader">The XmlReader to use.</param>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
using (Stream stream = response.GetResponseStream())
{
return (T)serializer.ReadObject(stream) as T;
}
return serializer.ReadObject(xmlReader) as T;
}
protected override void WriteInternal(object content, HttpWebRequest request)
/// <summary>
/// Abstract template method that writes the actual body using a <see cref="XmlWriter"/>. Invoked from <see cref="M:WriteInternal"/>.
/// </summary>
/// <param name="xmlWriter">The XmlWriter to use.</param>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteXml(XmlWriter xmlWriter, 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;
}
serializer.WriteObject(xmlWriter, content);
}
}
}

View File

@@ -27,23 +27,55 @@ using System.Xml.Linq;
namespace Spring.Http.Converters.Xml
{
// TODO : Support XElement.Load options
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write XML
/// from a <see cref="XElement"/> (Linq to XML).
/// </summary>
/// <remarks>
/// By default, this converter supports 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Bruno Baia</author>
public class XElementHttpMessageConverter : AbstractXmlHttpMessageConverter
{
/// <summary>
/// Creates a new instance of the <see cref="XElementHttpMessageConverter"/>
/// with 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// </summary>
public XElementHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
base()
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
protected override bool Supports(Type type)
{
return type.Equals(typeof(XElement));
}
/// <summary>
/// Abstract template method that reads the actualy object using a <see cref="XmlReader"/>. Invoked from <see cref="M:ReadInternal"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="xmlReader">The XmlReader to use.</param>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
return XElement.Load(xmlReader) as T;
}
/// <summary>
/// Abstract template method that writes the actual body using a <see cref="XmlWriter"/>. Invoked from <see cref="M:WriteInternal"/>.
/// </summary>
/// <param name="xmlWriter">The XmlWriter to use.</param>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
XElement xElement = content as XElement;

View File

@@ -24,18 +24,43 @@ using System.Net;
namespace Spring.Http.Converters.Xml
{
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write XML
/// from a <see cref="XmlDocument"/>.
/// </summary>
/// <remarks>
/// By default, this converter supports 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Bruno Baia</author>
public class XmlDocumentHttpMessageConverter : AbstractXmlHttpMessageConverter
{
/// <summary>
/// Creates a new instance of the <see cref="XmlDocumentHttpMessageConverter"/>
/// with 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// </summary>
public XmlDocumentHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
base()
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
protected override bool Supports(Type type)
{
return type.Equals(typeof(XmlDocument));
}
/// <summary>
/// Abstract template method that reads the actualy object using a <see cref="XmlReader"/>. Invoked from <see cref="M:ReadInternal"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="xmlReader">The XmlReader to use.</param>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
XmlDocument document = new XmlDocument();
@@ -43,6 +68,12 @@ namespace Spring.Http.Converters.Xml
return document as T;
}
/// <summary>
/// Abstract template method that writes the actual body using a <see cref="XmlWriter"/>. Invoked from <see cref="M:WriteInternal"/>.
/// </summary>
/// <param name="xmlWriter">The XmlWriter to use.</param>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
XmlDocument document = content as XmlDocument;

View File

@@ -23,18 +23,35 @@ using System.Xml;
using System.Net;
using System.Xml.Serialization;
using Spring.Util;
namespace Spring.Http.Converters.Xml
{
// TODO : Support for known types, etc...
/// <summary>
/// Implementation of <see cref="IHttpMessageConverter"/> that can read and write XML
/// using <see cref="XmlSerializer"/>.
/// </summary>
/// <remarks>
/// By default, this converter supports 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// This can be overridden by setting the <see cref="P:SupportedMediaTypes"/> property.
/// </remarks>
/// <author>Bruno Baia</author>
public class XmlSerializableHttpMessageConverter : AbstractXmlHttpMessageConverter
{
/// <summary>
/// Creates a new instance of the <see cref="XmlSerializableHttpMessageConverter"/>
/// with 'text/xml', 'application/xml', and 'application/*-xml' media types.
/// </summary>
public XmlSerializableHttpMessageConverter() :
base(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"))
base()
{
}
/// <summary>
/// Indicates whether the given class is supported by this converter.
/// </summary>
/// <param name="type">The type to test for support.</param>
/// <returns><see langword="true"/> if supported; otherwise <see langword="false"/></returns>
protected override bool Supports(Type type)
{
return true;
@@ -43,12 +60,25 @@ namespace Spring.Http.Converters.Xml
// AttributeUtils.FindAttribute(type, typeof(XmlTypeAttribute)) != null);
}
/// <summary>
/// Abstract template method that reads the actualy object using a <see cref="XmlReader"/>. Invoked from <see cref="M:ReadInternal"/>.
/// </summary>
/// <typeparam name="T">The type of object to return.</typeparam>
/// <param name="xmlReader">The XmlReader to use.</param>
/// <param name="response">The HTTP response to read from.</param>
/// <returns>The converted object.</returns>
protected override T ReadXml<T>(XmlReader xmlReader, HttpWebResponse response)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return serializer.Deserialize(xmlReader) as T;
}
/// <summary>
/// Abstract template method that writes the actual body using a <see cref="XmlWriter"/>. Invoked from <see cref="M:WriteInternal"/>.
/// </summary>
/// <param name="xmlWriter">The XmlWriter to use.</param>
/// <param name="content">The object to write to the HTTP request.</param>
/// <param name="request">The HTTP request to write to.</param>
protected override void WriteXml(XmlWriter xmlWriter, object content, HttpWebRequest request)
{
XmlSerializer serializer = new XmlSerializer(content.GetType());

View File

@@ -24,13 +24,11 @@ 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
*/
/// <summary>
/// Factory for <see cref="HttpWebRequest"/> objects. Requests are created by the <see cref="M:CreateRequest"/> method.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class DefaultHttpWebRequestFactory : IHttpWebRequestFactory
{
// TODO : Add other properties
@@ -40,6 +38,9 @@ namespace Spring.Http
private IWebProxy _proxy;
private int? _timeout;
/// <summary>
/// Gets or sets the collection of security certificates that are associated with this request.
/// </summary>
public X509CertificateCollection ClientCertificates
{
get
@@ -52,18 +53,34 @@ namespace Spring.Http
}
}
/// <summary>
/// Gets or sets authentication information for the request.
/// </summary>
public ICredentials Credentials
{
get { return _credentials; }
set { _credentials = value; }
}
/// <summary>
/// Gets or sets proxy information for the request.
/// </summary>
/// <remarks>
/// The default value is set by calling the <see cref="P:System.Net.GlobalProxySelection.Select"/> property.
/// </remarks>
public IWebProxy Proxy
{
get { return _proxy; }
set { _proxy = value; }
}
/// <summary>
/// Gets or sets the time-out value in milliseconds for the <see cref="M:System.Net.HttpWebRequest.GetResponse()"/>
/// and <see cref="M:System.Net.HttpWebRequest.GetRequestStream()"/> methods.
/// </summary>
/// <remarks>
/// The default is 100,000 milliseconds (100 seconds).
/// </remarks>
public int? Timeout
{
get { return _timeout; }
@@ -72,15 +89,11 @@ namespace Spring.Http
#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
*/
/// <summary>
/// Create a new <see cref="HttpWebRequest"/> for the specified URI.
/// </summary>
/// <param name="uri">The URI to create a request for.</param>
/// <returns>The created request</returns>
public HttpWebRequest CreateRequest(Uri uri)
{
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;

View File

@@ -1,19 +1,19 @@
#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.
/*
* 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
@@ -22,23 +22,52 @@ 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
*/
/// <summary>
/// Enumeration of HTTP request methods as defined in the HTTP specification.
/// <a href="http://tools.ietf.org/html/rfc2616#section-5.1.1">HTTP 1.1, section 6</a>
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia</author>
public enum HttpMethod
{
/// <summary>
/// The OPTIONS method.
/// </summary>
OPTIONS,
/// <summary>
/// The GET method.
/// </summary>
GET,
HEAD,
POST,
PUT,
DELETE,
/// <summary>
/// The HEAD method.
/// </summary>
HEAD,
/// <summary>
/// The POST method.
/// </summary>
POST,
/// <summary>
/// The PUT method.
/// </summary>
PUT,
/// <summary>
/// The DELETE method.
/// </summary>
DELETE,
/// <summary>
/// The TRACE method.
/// </summary>
TRACE,
/// <summary>
/// The CONNECT method.
/// </summary>
CONNECT
}
}

View File

@@ -22,7 +22,11 @@ using System.Net;
namespace Spring.Http
{
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5
/// <summary>
/// Represents a HTTP request message, as defined in the HTTP specification.
/// <a href="http://tools.ietf.org/html/rfc2616#section-5">HTTP 1.1, section 5</a>
/// </summary>
/// <author>Bruno Baia</author>
public class HttpRequestMessage
{
//private string requestUri;
@@ -43,87 +47,94 @@ namespace Spring.Http
// set { httpVersion = value; }
//}
/// <summary>
/// Gets the HTTP method.
/// </summary>
public HttpMethod Method
{
get { return this.method; }
set { this.method = value; }
}
/**
* Returns the headers of this message.
*/
/// <summary>
/// Gets the request headers.
/// </summary>
public WebHeaderCollection Headers
{
get { return this.headers; }
}
/**
* Returns the body of this message.
*/
/// <summary>
/// Gets the response body.
/// </summary>
public object Body
{
get { return this.body; }
}
/**
* Create a new {@code HttpRequestMessage} with no body and no headers.
*/
/// <summary>
/// Creates a new instance of <see cref="HttpRequestMessage"/> with the given Http method.
/// </summary>
/// <param name="method">The HTTP method.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpRequestMessage"/> with the given headers.
/// </summary>
/// <param name="headers">The request headers.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpRequestMessage"/> with the given headers and HTTP method.
/// </summary>
/// <param name="headers">The request headers.</param>
/// <param name="method">The HTTP method.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpRequestMessage"/> with the given body.
/// </summary>
/// <param name="body">The response body.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpRequestMessage"/> with the given body and HTTP method.
/// </summary>
/// <param name="body">The response body.</param>
/// <param name="method">The HTTP method.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpRequestMessage"/> with the given body and headers.
/// </summary>
/// <param name="body">The response body.</param>
/// <param name="headers">The response headers.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpRequestMessage"/> with the given body, headers and HTTP method.
/// </summary>
/// <param name="body">The response body.</param>
/// <param name="headers">The response headers.</param>
/// <param name="method">The HTTP method.</param>
public HttpRequestMessage(object body, WebHeaderCollection headers, HttpMethod method)
{
this.method = method;

View File

@@ -22,86 +22,31 @@ using System.Net;
namespace Spring.Http
{
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6
public class HttpResponseMessage<T> where T : class
/// <summary>
/// Represents a HTTP response message with no entity.
/// </summary>
/// <author>Bruno Baia</author>
public class HttpResponseMessage : HttpResponseMessage<object>
{
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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpResponseMessage"/> with the given status code and status description.
/// </summary>
/// <param name="statusCode">The HTTP status code.</param>
/// <param name="statusDescription">The HTTP status description.</param>
public HttpResponseMessage(HttpStatusCode statusCode, string statusDescription) :
this(null, null, statusCode, statusDescription)
base(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
*/
/// <summary>
/// Creates a new instance of <see cref="HttpResponseMessage"/> with the given headers, status code and status description.
/// </summary>
/// <param name="headers">The response headers.</param>
/// <param name="statusCode">The HTTP status code.</param>
/// <param name="statusDescription">The HTTP status description.</param>
public HttpResponseMessage(WebHeaderCollection headers, HttpStatusCode statusCode, string statusDescription) :
this(null, headers, statusCode, statusDescription)
base(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,117 @@
#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
{
/// <summary>
/// Represents a HTTP response message, as defined in the HTTP specification.
/// <a href="http://tools.ietf.org/html/rfc2616#section-6">HTTP 1.1, section 6</a>
/// </summary>
/// <typeparam name="T">The type of the response body.</typeparam>
/// <author>Bruno Baia</author>
public class HttpResponseMessage<T> where T : class
{
private WebHeaderCollection headers;
private T body;
private HttpStatusCode statusCode;
private string statusDescription;
/// <summary>
/// Gets the response headers.
/// </summary>
public WebHeaderCollection Headers
{
get { return this.headers; }
}
/// <summary>
/// Gets the response body. May be null.
/// </summary>
public T Body
{
get { return this.body; }
}
/// <summary>
/// Gets the HTTP status code of the response.
/// </summary>
public HttpStatusCode StatusCode
{
get { return statusCode; }
}
/// <summary>
/// Gets the HTTP status description of the response.
/// </summary>
public string StatusDescription
{
get { return statusDescription; }
}
/// <summary>
/// Creates a new instance of <see cref="HttpResponseMessage{T}"/> with the given status code and status description.
/// </summary>
/// <param name="statusCode">The HTTP status code.</param>
/// <param name="statusDescription">The HTTP status description.</param>
public HttpResponseMessage(HttpStatusCode statusCode, string statusDescription) :
this(null, null, statusCode, statusDescription)
{
}
/// <summary>
/// Creates a new instance of <see cref="HttpResponseMessage{T}"/> with the given body, status code and status description.
/// </summary>
/// <param name="body">The response body.</param>
/// <param name="statusCode">The HTTP status code.</param>
/// <param name="statusDescription">The HTTP status description.</param>
public HttpResponseMessage(T body, HttpStatusCode statusCode, string statusDescription) :
this(body, null, statusCode, statusDescription)
{
}
/// <summary>
/// Creates a new instance of <see cref="HttpResponseMessage{T}"/> with the given headers, status code and status description.
/// </summary>
/// <param name="headers">The response headers.</param>
/// <param name="statusCode">The HTTP status code.</param>
/// <param name="statusDescription">The HTTP status description.</param>
public HttpResponseMessage(WebHeaderCollection headers, HttpStatusCode statusCode, string statusDescription) :
this(null, headers, statusCode, statusDescription)
{
}
/// <summary>
/// Creates a new instance of <see cref="HttpResponseMessage{T}"/> with the given body, headers, status code and status description.
/// </summary>
/// <param name="body">The response body.</param>
/// <param name="headers">The response headers.</param>
/// <param name="statusCode">The HTTP status code.</param>
/// <param name="statusDescription">The HTTP status description.</param>
public HttpResponseMessage(T body, WebHeaderCollection headers, HttpStatusCode statusCode, string statusDescription)
{
this.statusCode = statusCode;
this.statusDescription = statusDescription;
this.body = body;
this.headers = headers;
}
}
}

View File

@@ -23,24 +23,18 @@ 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
*/
/// <summary>
/// Factory for <see cref="HttpWebRequest"/> objects. Requests are created by the <see cref="M:CreateRequest"/> method.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
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
*/
/// <summary>
/// Create a new <see cref="HttpWebRequest"/> for the specified URI.
/// </summary>
/// <param name="uri">The URI to create a request for.</param>
/// <returns>The created request</returns>
HttpWebRequest CreateRequest(Uri uri);
}
}

View File

@@ -28,88 +28,88 @@ 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>
*/
/// <summary>
/// Represents an Internet Media Type, as defined in the HTTP specification.
/// <a href="http://tools.ietf.org/html/rfc2616#section-3.7">HTTP 1.1, section 3.7</a>
/// </summary>
/// <remarks>
/// Consists of a <see cref="P:Type"/> and a <see cref="P:SubType"/>.
/// Also has functionality to parse media types from a string using <see cref="M:ParseMediaType(string)"/>,
/// or multiple comma-separated media types using <see cref="M:ParseMediaTypes(string)"/>.
/// </remarks>
/// <author>Arjen Poutsma</author>
/// <author>Juergen Hoeller</author>
/// <author>Bruno Baia (.NET)</author>
public class MediaType : IComparable<MediaType>
{
/**
* Public constant media type that includes all media ranges (i.e. <code>&#42;/&#42;</code>).
*/
/// <summary>
/// Public constant media type that includes all media ranges (i.e. '*/*').
/// </summary>
public static readonly MediaType ALL = new MediaType("*", "*");
/**
* Public constant media type for {@code application/atom+xml}.
*/
/// <summary>
/// Public constant media type for 'application/atom+xml'.
/// </summary>
public static readonly MediaType APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
/**
* Public constant media type for {@code application/x-www-form-urlencoded}.
* */
/// <summary>
/// Public constant media type for 'application/x-www-form-urlencoded'.
/// </summary>
public static readonly MediaType APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
/**
* Public constant media type for {@code application/json}.
* */
/// <summary>
/// Public constant media type for 'application/json'.
/// </summary>
public static readonly MediaType APPLICATION_JSON = new MediaType("application", "json");
/**
* Public constant media type for {@code application/octet-stream}.
* */
/// <summary>
/// Public constant media type for 'application/octet-stream'.
/// </summary>
public static readonly MediaType APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
/**
* Public constant media type for {@code application/xhtml+xml}.
* */
/// <summary>
/// Public constant media type for 'application/xhtml+xml'.
/// </summary>
public static readonly MediaType APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
/**
* Public constant media type for {@code image/gif}.
*/
/// <summary>
/// Public constant media type for 'image/gif'.
/// </summary>
public static readonly MediaType IMAGE_GIF = new MediaType("image", "gif");
/**
* Public constant media type for {@code image/jpeg}.
*/
/// <summary>
/// Public constant media type for 'image/jpeg'.
/// </summary>
public static readonly MediaType IMAGE_JPEG = new MediaType("image", "jpeg");
/**
* Public constant media type for {@code image/png}.
*/
/// <summary>
/// Public constant media type for 'image/png'.
/// </summary>
public static readonly MediaType IMAGE_PNG = new MediaType("image", "png");
/**
* Public constant media type for {@code image/xml}.
*/
/// <summary>
/// Public constant media type for 'image/xml'.
/// </summary>
public static readonly MediaType APPLICATION_XML = new MediaType("application", "xml");
/**
* Public constant media type for {@code multipart/form-data}.
* */
/// <summary>
/// Public constant media type for 'multipart/form-data'.
/// </summary>
public static readonly MediaType MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
/**
* Public constant media type for {@code text/html}.
* */
/// <summary>
/// Public constant media type for 'text/html'.
/// </summary>
public static readonly MediaType TEXT_HTML = new MediaType("text", "html");
/**
* Public constant media type for {@code text/plain}.
* */
/// <summary>
/// Public constant media type for 'text/plain'.
/// </summary>
public static readonly MediaType TEXT_PLAIN = new MediaType("text", "plain");
/**
* Public constant media type for {@code text/xml}.
* */
/// <summary>
/// Public constant media type for 'text/xml'.
/// </summary>
public static readonly MediaType TEXT_XML = new MediaType("text", "xml");
@@ -125,43 +125,41 @@ namespace Spring.Http
private IDictionary<string, string> parameters;
/**
* Return the primary type.
*/
/// <summary>
/// Gets the primary type.
/// </summary>
public string Type
{
get { return this.type; }
}
/**
* Return the subtype.
*/
/// <summary>
/// Gets the subtype.
/// </summary>
public string Subtype
{
get { return this.subtype; }
}
/**
* Indicate whether the {@linkplain #getType() type} is the wildcard character <code>&#42;</code> or not.
*/
/// <summary>
/// Indicate whether the type is the wildcard character '*', or not.
/// </summary>
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>
*/
/// <summary>
/// Indicate whether the subtype is the wildcard character '*', or not.
/// </summary>
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
*/
/// <summary>
/// Gets the character set, as indicated by a 'charset' parameter, if any.
/// </summary>
public string CharSet
{
get
@@ -174,11 +172,10 @@ namespace Spring.Http
}
}
/**
* Return the quality value, as indicated by a <code>q</code> parameter, if any.
* Defaults to <code>1.0</code>.
* @return the quality factory
*/
/// <summary>
/// Gets the quality value, as indicated by a 'q' parameter, if any.
/// Defaults to '1.0'.
/// </summary>
public double QualityValue
{
get
@@ -190,75 +187,68 @@ namespace Spring.Http
}
}
/**
* 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
*/
/// <summary>
/// Creates a new instance of <see cref="MediaType"/> for the given primary type.
/// The subtype is set to '*', parameters are empty.
/// </summary>
/// <param name="type">The primary type.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="MediaType"/> for the given primary type and subtype.
/// The parameters are empty.
/// </summary>
/// <param name="type">The primary type.</param>
/// <param name="subtype">The subtype.</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="MediaType"/> for the given primary type, subtype and character set.
/// </summary>
/// <param name="type">The primary type.</param>
/// <param name="subtype">The subtype.</param>
/// <param name="charSet">The character set</param>
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
*/
/// <summary>
/// Creates a new instance of <see cref="MediaType"/> for the given primary type, subtype and quality value.
/// </summary>
/// <param name="type">The primary type.</param>
/// <param name="subtype">The subtype.</param>
/// <param name="qualityValue">The quality value</param>
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)
/// <summary>
/// Creates a new instance of <see cref="MediaType"/> by copying the type and subtype of the given MediaType,
/// and allows for different parameter.
/// </summary>
/// <param name="otherMediaType">The other media type.</param>
/// <param name="parameters">The parameters, may be null.</param>
public MediaType(MediaType otherMediaType, IDictionary<string, string> parameters) :
this(otherMediaType.Type, otherMediaType.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
*/
/// <summary>
/// Creates a new instance of <see cref="MediaType"/> for the given primary type, subtype and parameters.
/// </summary>
/// <param name="type">The primary type.</param>
/// <param name="subtype">The subtype.</param>
/// <param name="parameters">The parameters, may be null.</param>
public MediaType(string type, string subtype, IDictionary<string, string> parameters)
{
AssertUtils.ArgumentHasText(type, "'type' must not be empty");
@@ -285,6 +275,15 @@ namespace Spring.Http
//}
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <param name="obj">
/// The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.
/// </param>
/// <returns>
/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
/// </returns>
public override bool Equals(object obj)
{
if (this == obj)
@@ -314,6 +313,15 @@ namespace Spring.Http
return false;
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <remarks>
/// <see cref="M:System.Object.GetHashCode"/> is suitable for use in hashing algorithms and data structures like a hash table.
/// </remarks>
/// <returns>
/// A hash code for the current <see cref="T:System.Object"/>.
/// </returns>
public override int GetHashCode()
{
int result = this.type.GetHashCode();
@@ -322,6 +330,12 @@ namespace Spring.Http
return result;
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object."/>
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object."/>.
/// </returns>
public override string ToString()
{
StringBuilder builder = new StringBuilder();
@@ -338,11 +352,11 @@ namespace Spring.Http
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>
*/
// **
// * 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);
@@ -382,26 +396,31 @@ namespace Spring.Http
// 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
*/
/// <summary>
/// Return a generic parameter value, given a parameter name.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns>The parameter value; or null if not present.</returns>
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)
/// <summary>
/// Indicate whether this <see cref="T:MediaType"/> includes the given media type.
/// </summary>
/// <remarks>
/// For instance, 'text/*' includes 'text/plain', 'text/html', and
/// 'application/*+xml' includes 'application/soap+xml', etc.
/// This method is non-symmetric.
/// </remarks>
/// <param name="otherMediaType">The reference media type with which to compare.</param>
/// <returns>
/// <see langword="true"/> if this media type includes the given media type; otherwise <see langword="false"/>.
/// </returns>
public bool Includes(MediaType otherMediaType)
{
if (other == null)
if (otherMediaType == null)
{
return false;
}
@@ -410,21 +429,21 @@ namespace Spring.Http
// */* includes anything
return true;
}
else if (this.type == other.type)
else if (this.type == otherMediaType.type)
{
if (this.subtype == other.subtype || this.IsWildcardSubtype)
if (this.subtype == otherMediaType.subtype || this.IsWildcardSubtype)
{
return true;
}
// application/*+xml includes application/soap+xml
int thisPlusIdx = this.subtype.IndexOf('+');
int otherPlusIdx = other.subtype.IndexOf('+');
int otherPlusIdx = otherMediaType.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);
string otherSubtypeSuffix = otherMediaType.subtype.Substring(otherPlusIdx + 1);
if (thisSubtypeSuffix == otherSubtypeSuffix && WILDCARD_TYPE == thisSubtypeNoSuffix)
{
return true;
@@ -434,39 +453,43 @@ namespace Spring.Http
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)
/// <summary>
/// Indicate whether this <see cref="T:MediaType"/> is compatible with the given media type.
/// </summary>
/// <remarks>
/// For instance, 'text/*' is compatible 'text/plain', 'text/html', and vice versa.
/// In effect, this method is similar to <see cref="M:Includes(MediaType)"/>, except that it's symmetric.
/// </remarks>
/// <param name="otherMediaType">The reference media type with which to compare.</param>
/// <returns>
/// <see langword="true"/> if this media type is compatible with the given media type; otherwise <see langword="false"/>.
/// </returns>
public bool IsCompatibleWith(MediaType otherMediaType)
{
if (other == null)
if (otherMediaType == null)
{
return false;
}
if (this.IsWildcardType || other.IsWildcardType)
if (this.IsWildcardType || otherMediaType.IsWildcardType)
{
return true;
}
else if (this.type == other.type)
else if (this.type == otherMediaType.type)
{
if (this.subtype == other.subtype || this.IsWildcardSubtype || other.IsWildcardSubtype)
if (this.subtype == otherMediaType.subtype || this.IsWildcardSubtype || otherMediaType.IsWildcardSubtype)
{
return true;
}
// application/*+xml is compatible with application/soap+xml, and vice-versa
int thisPlusIdx = this.subtype.IndexOf('+');
int otherPlusIdx = other.subtype.IndexOf('+');
int otherPlusIdx = otherMediaType.subtype.IndexOf('+');
if (thisPlusIdx != -1 && otherPlusIdx != -1)
{
string thisSubtypeNoSuffix = this.subtype.Substring(0, thisPlusIdx);
string otherSubtypeNoSuffix = other.subtype.Substring(0, otherPlusIdx);
string otherSubtypeNoSuffix = otherMediaType.subtype.Substring(0, otherPlusIdx);
string thisSubtypeSuffix = this.subtype.Substring(thisPlusIdx + 1);
string otherSubtypeSuffix = other.subtype.Substring(otherPlusIdx + 1);
string otherSubtypeSuffix = otherMediaType.subtype.Substring(otherPlusIdx + 1);
if (thisSubtypeSuffix == otherSubtypeSuffix &&
(WILDCARD_TYPE == thisSubtypeNoSuffix || WILDCARD_TYPE == otherSubtypeNoSuffix))
@@ -480,11 +503,16 @@ namespace Spring.Http
#region IComparable<MediaType> Membres
/**
* Compares this {@link MediaType} to another alphabetically.
* @param other media type to compare to
* @see #sortBySpecificity(List)
*/
/// <summary>
/// Compares this <see cref="MediaType"/> to another alphabetically.
/// </summary>
/// <param name="other">The media type to compare with this object.</param>
/// <returns>
/// A 32-bit signed integer that indicates the relative order of the objects
/// being compared. The return value has the following meanings: Value Meaning
/// Less than zero This object is less than the other parameter. Zero This object
/// is equal to other. Greater than zero This object is greater than other.
/// </returns>
public int CompareTo(MediaType other)
{
int comp = this.type.CompareTo(other.type);
@@ -519,15 +547,17 @@ namespace Spring.Http
#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
*/
/// <summary>
/// Parse the given String into a single <see cref="MediaType"/>.
/// </summary>
/// <param name="mediaType">The string to parse.</param>
/// <returns>The media type.</returns>
public static MediaType ParseMediaType(string mediaType)
{
AssertUtils.ArgumentHasText(mediaType, "'mediaType' must not be empty");
if (!StringUtils.HasText(mediaType))
{
return null;
}
string[] parts = mediaType.Split(';');
string fullType = parts[0].Trim();
@@ -570,17 +600,18 @@ namespace Spring.Http
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
*/
/// <summary>
/// Parse the given, comma-seperated string into a list of <see cref="MediaType"/> objects.
/// </summary>
/// <remarks>
/// This method can be used to parse an 'Accept' or 'Content-Type' header.
/// </remarks>
/// <param name="mediaTypes">The string to parse.</param>
/// <returns>The list of media types.</returns>
public static List<MediaType> ParseMediaTypes(string mediaTypes)
{
List<MediaType> mediaTypeList = new List<MediaType>();
if (!StringUtils.HasLength(mediaTypes))
if (!StringUtils.HasText(mediaTypes))
{
return mediaTypeList;
}
@@ -592,13 +623,14 @@ namespace Spring.Http
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
*/
/// <summary>
/// Return a string representation of the given list of <see cref="MediaType"/> objects.
/// </summary>
/// <remarks>
/// This method can be used to for an 'Accept' or 'Content-Type' header.
/// </remarks>
/// <param name="mediaTypes">The list of media types to convert.</param>
/// <returns>The string representation of the given list.</returns>
public static string ToString(IEnumerable<MediaType> mediaTypes)
{
StringBuilder builder = new StringBuilder();
@@ -613,32 +645,38 @@ namespace Spring.Http
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>
*/
/// <summary>
/// Sorts the given list of <see cref="MediaType"/> objects by specificity.
/// <a href="http://tools.ietf.org/html/rfc2616#section-14.1">HTTP 1.1, section 14.1</a>
/// </summary>
/// <remarks>
/// <para>
/// Given two media types:
/// <ol>
/// <li>if either media type has a wildcard type, then the media type without the
/// wildcard is ordered before the other.</li>
/// <li>if the two media types have different types, then they are considered equal and
/// remain their current order.</li>
/// <li>if either media type has a wildcard subtype, then the media type without
/// the wildcard is sorted before the other.</li>
/// <li>if the two media types have different subtypes, then they are considered equal
/// and remain their current order.</li>
/// <li>if the two media types have different 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 parameters, then the
/// media type with the most parameters is ordered before the other.</li>
/// </ol>
/// </para>
/// <para>
/// 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>
/// </para>
/// </remarks>
/// <param name="mediaTypes">The list of media types to be sorted.</param>
public static void SortBySpecificity(List<MediaType> mediaTypes)
{
AssertUtils.ArgumentNotNull(mediaTypes, "mediaTypes");
@@ -649,26 +687,29 @@ namespace Spring.Http
}
}
/**
* 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()
*/
/// <summary>
/// Sorts the given list of <see cref="MediaType"/> objects by quality value.
/// </summary>
/// <remarks>
/// <para>
/// Given two media types:
/// <ol>
/// <li>if the two media types have different quality value, then the media type
/// with the highest quality value is ordered before the other.</li>
/// <li>if either media type has a wildcard type, then the media type without the
/// wildcard is ordered before the other.</li>
/// <li>if the two media types have different types, then they are considered equal and
/// remain their current order.</li>
/// <li>if either media type has a wildcard subtype, then the media type without
/// the wildcard is sorted before the other.</li>
/// <li>if the two media types have different subtypes, then they are considered equal
/// and remain their current order.</li>
/// <li>if the two media types have a different amount of parameters, then the
/// media type with the most parameters is ordered before the other.</li>
/// </ol>
/// </para>
/// </remarks>
/// <param name="mediaTypes">The list of media types to be sorted</param>
public static void SortByQualityValue(List<MediaType> mediaTypes)
{
AssertUtils.ArgumentNotNull(mediaTypes, "mediaTypes");
@@ -679,7 +720,14 @@ namespace Spring.Http
}
}
/// <summary>
/// <see cref="IComparer&lt;MediaType>"/> implementation by specificity value.
/// </summary>
public static IComparer<MediaType> SPECIFICITY_COMPARER = new SpecificityComparer();
/// <summary>
/// <see cref="IComparer&lt;MediaType>"/> implementation by quality value.
/// </summary>
public static IComparer<MediaType> QUALITY_VALUE_COMPARER = new QualityValueComparer();
#region SpecificityComparer

View File

@@ -23,25 +23,31 @@ 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
*/
/// <summary>
/// Callback interface for code that operates on a <see cref="HttpWebRequest"/>.
/// Allows to manipulate the request headers, and write to the request body.
/// </summary>
/// <remarks>
/// <para>
/// Callback interface used by <see cref="RestTemplate"/>'s senders methods.
/// Implementations of this interface perform the actual work of writing data
/// to a <see cref="HttpWebRequest"/>, but don't need to worry about exception
/// handling or closing resources.
/// </para>
/// <para>
/// Used internally by the <see cref="RestTemplate"/>, but also useful for application code.
/// </para>
/// </remarks>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
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
*/
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebRequest"/> to write data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="request">The active HTTP request.</param>
void DoWithRequest(HttpWebRequest request);
}
}

View File

@@ -23,26 +23,31 @@ 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
*/
/// <summary>
/// Callback interface for code that operates on a <see cref="HttpWebResponse"/>.
/// Allows to manipulate the response headers, and extract the response body.
/// </summary>
/// <remarks>
/// <para>
/// Generic callback interface used by <see cref="RestTemplate"/>'s retrieval methods.
/// Implementations of this interface perform the actual work of extracting data
/// from a <see cref="HttpWebResponse"/>, but don't need to worry about exception
/// handling or closing resources.
/// </para>
/// <para>
/// Used internally by the <see cref="RestTemplate"/>, but also useful for application code.
/// </para>
/// </remarks>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
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;
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebResponse"/> to extract data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="response">The active HTTP request.</param>
T ExtractData(HttpWebResponse response);
}
}

View File

@@ -26,413 +26,559 @@ using Spring.Http;
namespace Spring.Http.Rest
{
/// <summary>
/// Interface specifying a basic set of RESTful operations.
/// </summary>
/// <remarks>
/// Not often used directly, but a useful option to enhance testability, as it can easily be mocked or stubbed.
/// </remarks>
/// <see cref="RestTemplate"/>
/// <author>Arjen Poutsma</author>
/// <author>Juergen Hoeller</author>
/// <author>Bruno Baia (.NET)</author>
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;
/// <summary>
/// Retrieve a representation by doing a GET on the specified URL.
/// The response (if any) is converted and returned.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The converted object</returns>
T GetForObject<T>(string url, params string[] uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Retrieve a representation by doing a GET on the specified URL.
/// The response (if any) is converted and returned.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The converted object</returns>
T GetForObject<T>(string url, IDictionary<string, string> uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Retrieve a representation by doing a GET on the specified URL.
/// The response (if any) is converted and returned.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <returns>The converted object</returns>
T GetForObject<T>(Uri url) where T : class;
/**
* 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;
/// <summary>
/// Retrieve an entity by doing a GET on the specified URL.
/// The response is converted and stored in an <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> GetForMessage<T>(string url, params string[] uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Retrieve an entity by doing a GET on the specified URL.
/// The response is converted and stored in an <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> GetForMessage<T>(string url, IDictionary<string, string> uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Retrieve an entity by doing a GET on the specified URL.
/// The response is converted and stored in an <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> GetForMessage<T>(Uri url) where T : class;
#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;
/// <summary>
/// Retrieve all headers of the resource specified by the URI template.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>All HTTP headers of that resource</returns>
WebHeaderCollection HeadForHeaders(string url, params string[] uriVariables);
/**
* 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;
/// <summary>
/// Retrieve all headers of the resource specified by the URI template.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>All HTTP headers of that resource</returns>
WebHeaderCollection HeadForHeaders(string url, IDictionary<string, string> uriVariables);
/**
* Retrieve all headers of the resource specified by the URL.
* @param url the URL
* @return all HTTP headers of that resource
*/
/// <summary>
/// Retrieve all headers of the resource specified by the URI template.
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>All HTTP headers of that resource</returns>
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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the value of the 'Location' header.
/// This header typically indicates where the new resource is stored.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The value for the Location header.</returns>
Uri PostForLocation(string url, object request, params string[] uriVariables);
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the value of the 'Location' header.
/// This header typically indicates where the new resource is stored.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The value for the Location header.</returns>
Uri PostForLocation(string url, object request, IDictionary<string, string> uriVariables);
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the value of the 'Location' header.
/// This header typically indicates where the new resource is stored.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The value for the Location header.</returns>
Uri PostForLocation(Uri url, object request);
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the representation found in the response.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The converted object.</returns>
T PostForObject<T>(string url, object request, params string[] uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the representation found in the response.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The converted object.</returns>
T PostForObject<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the representation found in the response.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The converted object.</returns>
T PostForObject<T>(Uri url, object request) where T : class;
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> PostForMessage<T>(string url, object request, params string[] uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> PostForMessage<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> PostForMessage<T>(Uri url, object request) where T : class;
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message with no entity.</returns>
HttpResponseMessage PostForMessage(string url, object request, params string[] uriVariables);
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message with no entity.</returns>
HttpResponseMessage PostForMessage(string url, object request, IDictionary<string, string> uriVariables);
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The HTTP response message with no entity.</returns>
HttpResponseMessage PostForMessage(Uri url, object request);
#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;
/// <summary>
/// Create or update a resource by PUTting the given object to the URI.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be PUT, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
void Put(string url, object request, params string[] uriVariables);
/**
* 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;
/// <summary>
/// Create or update a resource by PUTting the given object to the URI.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be PUT, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
void Put(string url, object request, IDictionary<string, string> uriVariables);
/**
* 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;
/// <summary>
/// Create or update a resource by PUTting the given object to the URI.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be PUT, may be null.</param>
void Put(Uri url, object request);
#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;
/// <summary>
/// Delete the resources at the specified URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
void Delete(string url, params string[] uriVariables);
/**
* 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;
/// <summary>
/// Delete the resources at the specified URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
void Delete(string url, IDictionary<string, string> uriVariables);
/**
* Delete the resources at the specified URL.
* @param url the URL
*/
void Delete(Uri url); //throws RestClientException;
/// <summary>
/// Delete the resources at the specified URI.
/// </summary>
/// <param name="url">The URL.</param>
void Delete(Uri url);
#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;
/// <summary>
/// Return the value of the Allow header for the given URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The value of the allow header.</returns>
IList<HttpMethod> OptionsForAllow(string url, params string[] uriVariables);
/**
* 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;
/// <summary>
/// Return the value of the Allow header for the given URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The value of the allow header.</returns>
IList<HttpMethod> OptionsForAllow(string url, IDictionary<string, string> uriVariables);
/**
* 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;
/// <summary>
/// Return the value of the Allow header for the given URI.
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>The value of the allow header.</returns>
IList<HttpMethod> OptionsForAllow(Uri url);
#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;
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> Exchange<T>(string url, HttpRequestMessage requestMessage, params string[] uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> Exchange<T>(string url, HttpRequestMessage requestMessage, IDictionary<string, string> uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <returns>The HTTP response message.</returns>
HttpResponseMessage<T> Exchange<T>(Uri url, HttpRequestMessage requestMessage) where T : class;
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message with no entity.</returns>
HttpResponseMessage Exchange(string url, HttpRequestMessage requestMessage, params string[] uriVariables);
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message with no entity.</returns>
HttpResponseMessage Exchange(string url, HttpRequestMessage requestMessage, IDictionary<string, string> uriVariables);
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <returns>The HTTP response message with no entity.</returns>
HttpResponseMessage Exchange(Uri url, HttpRequestMessage requestMessage);
#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}
*/
/// <summary>
/// Execute the HTTP request to the given URI template, preparing the request with the
/// <see cref="IRequestCallback"/>, and reading the response with an <see cref="IResponseExtractor{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestCallback">Object that prepares the request.</param>
/// <param name="responseExtractor">Object that extracts the return value from the response.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>An arbitrary object, as returned by the <see cref="IResponseExtractor{T}"/>.</returns>
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;
/// <summary>
/// Execute the HTTP request to the given URI template, preparing the request with the
/// <see cref="IRequestCallback"/>, and reading the response with an <see cref="IResponseExtractor{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestCallback">Object that prepares the request.</param>
/// <param name="responseExtractor">Object that extracts the return value from the response.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>An arbitrary object, as returned by the <see cref="IResponseExtractor{T}"/>.</returns>
T Execute<T>(string url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor, IDictionary<string, string> uriVariables) where T : class;
/**
* 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;
/// <summary>
/// Execute the HTTP request to the given URI template, preparing the request with the
/// <see cref="IRequestCallback"/>, and reading the response with an <see cref="IResponseExtractor{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestCallback">Object that prepares the request.</param>
/// <param name="responseExtractor">Object that extracts the return value from the response.</param>
/// <returns>An arbitrary object, as returned by the <see cref="IResponseExtractor{T}"/>.</returns>
T Execute<T>(Uri url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor) where T : class;
#endregion
}

View File

@@ -23,19 +23,23 @@ using System.Runtime.Serialization;
namespace Spring.Http.Rest
{
/// <summary>
/// Base class for exceptions thrown by <see cref="RestTemplate"/> whenever it encounters client-side HTTP errors.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
[Serializable]
public class RestClientException : Exception
{
/// <summary>
/// Creates a new instance of the
/// RestClientException class.
/// Creates a new instance of the <see cref="RestClientException"/> class.
/// </summary>
public RestClientException()
{
}
/// <summary>
/// Creates a new instance of the RestClientException class.
/// Creates a new instance of the <see cref="RestClientException"/> class.
/// </summary>
/// <param name="message">
/// A message about the exception.
@@ -46,7 +50,7 @@ namespace Spring.Http.Rest
}
/// <summary>
/// Creates a new instance of the RestClientException class.
/// Creates a new instance of the <see cref="RestClientException"/> class.
/// </summary>
/// <param name="message">
/// A message about the exception.
@@ -60,7 +64,7 @@ namespace Spring.Http.Rest
}
/// <summary>
/// Creates a new instance of the RestClientException class.
/// Creates a new instance of the <see cref="RestClientException"/> class.
/// </summary>
/// <param name="info">
/// The <see cref="System.Runtime.Serialization.SerializationInfo"/>

View File

@@ -23,7 +23,6 @@ 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;
@@ -31,73 +30,73 @@ 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
using AssertUtils = Spring.Util.AssertUtils;
using StringUtils = Spring.Util.StringUtils;
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
*/
/// <summary>
/// The central class for client-side HTTP access.
/// 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.
/// </summary>
/// <remarks>
/// <para>
/// 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><see cref="M:Delete"/></td></tr>
/// <tr><td>GET</td><td><see cref="M:GetForObject"/></td></tr>
/// <tr><td></td><td><see cref="M:GetForMessage"/></td></tr>
/// <tr><td>HEAD</td><td><see cref="M:HeadForHeaders"/></td></tr>
/// <tr><td>OPTIONS</td><td><see cref="M:OptionsForAllow"/></td></tr>
/// <tr><td>POST</td><td><see cref="M:PostForLocation"/></td></tr>
/// <tr><td></td><td><see cref="M:PostForObject"/></td></tr>
/// <tr><td>PUT</td><td><see cref="M:Put"/></td></tr>
/// <tr><td>any</td><td><see cref="M:Exchange"/></td></tr>
/// <tr><td></td><td><see cref="M:Execute"/></td></tr>
/// </table>
/// </para>
/// <para>
/// For each of these HTTP methods, there are three corresponding Java methods in the <see cref="RestTemplate"/>.
/// Two variant take a string URI as first argument and are capable of substituting any URI templates in
/// that URL using either a string variable arguments array, or a string dictionary.
/// The string varargs variant expands the given template variables in order, so that
/// <code>
/// string result = restTemplate.GetForObject&lt;string>("http://example.com/hotels/{hotel}/bookings/{booking}", "42", "21");
/// </code>
/// will perform a GET on '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:
/// <code>
/// IDictionary&lt;String, String&gt; vars = new Dictionary&lt;String, String&gt;();
/// vars.Add("hotel", "42");
/// string result = restTemplate.GetForObject&lt;string>("http://example.com/hotels/{hotel}/rooms/{hotel}", vars);
/// </code>
/// will perform a GET on 'http://example.com/hotels/42/rooms/42'. Alternatively, there are URI variant
/// methods, which do not allow for URI templates, but allow you to reuse a single, expanded URI multiple times.
/// </para>
/// <para>
/// Furthermore, the string-argument methods assume that the URL String is unencoded. This means that
/// <code>
/// restTemplate.GetForObject&lt;string>("http://example.com/hotel list");
/// </code>
/// will perform a GET on 'http://example.com/hotel%20list'.
/// </para>
/// <para>
/// Objects passed to and returned from these methods are converted to and from HTTP messages by
/// <see cref="IHttpMessageConverter"/> instances. Converters for the main mime types are registered by default,
/// but you can also write your own converter and register it via the <see cref="P:MessageConverters"/> property.
/// </para>
/// <para>
/// This template uses a <see cref="T:DefaultHttpWebRequestFactory"/> as default strategy for creating HTTP connections.
/// </para>
/// </remarks>
/// <see cref="IHttpMessageConverter"/>
/// <see cref="IRequestCallback"/>
/// <see cref="IResponseExtractor{T}"/>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class RestTemplate : IRestOperations
{
#region Logging
@@ -116,6 +115,9 @@ namespace Spring.Http.Rest
private IResponseExtractor<WebHeaderCollection> headersExtractor;
/// <summary>
/// Gets or sets the base URL for the request.
/// </summary>
public Uri BaseAddress
{
get
@@ -133,18 +135,29 @@ namespace Spring.Http.Rest
}
}
/// <summary>
/// Indicates if an exception is thrown when a HTTP client or server error happens (HTTP status code 3xx or 4xx).
/// By default, the value is <see langword="true"/> to be consistent with .NET behavior in <see cref="M:HttpWebRequest.GetResponse()"/>.
/// </summary>
public bool ThrowExceptionOnError
{
get { return _throwExceptionOnError; }
set { _throwExceptionOnError = value; }
}
/// <summary>
/// Gets or sets the message converters.
/// These converters are used to convert from and to HTTP request and response messages.
/// </summary>
public IList<IHttpMessageConverter> MessageConverters
{
get { return this._messageConverters; }
set { this._messageConverters = value; }
}
/// <summary>
/// Gets or sets the request factory that this class uses for obtaining for <see cref="HttpWebRequest"/> objects.
/// </summary>
public IHttpWebRequestFactory RequestFactory
{
get { return this._requestFactory; }
@@ -155,19 +168,29 @@ namespace Spring.Http.Rest
#region Constructor(s)
/// <summary>
/// Creates a new instance of <see cref="RestTemplate"/>.
/// </summary>
/// <param name="baseAddress">The base address to use.</param>
public RestTemplate(Uri baseAddress) :
this()
{
this.BaseAddress = baseAddress;
}
/// <summary>
/// Creates a new instance of <see cref="RestTemplate"/>.
/// </summary>
/// <param name="baseAddress">The base address to use.</param>
public RestTemplate(string baseAddress) :
this()
{
this.BaseAddress = new Uri(baseAddress, UriKind.Absolute);
}
/** Create a new instance of the {@link RestTemplate} using default settings. */
/// <summary>
/// Creates a new instance of <see cref="RestTemplate"/>.
/// </summary>
public RestTemplate()
{
this._throwExceptionOnError = true;
@@ -175,22 +198,35 @@ namespace Spring.Http.Rest
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());
this._messageConverters.Add(new StringHttpMessageConverter());
//this._messageConverters.Add(new XmlSerializableHttpMessageConverter());
this._messageConverters.Add(new XmlDocumentHttpMessageConverter());
#if NET_3_5
this._messageConverters.Add(new DataContractHttpMessageConverter());
this._messageConverters.Add(new XElementHttpMessageConverter());
this._messageConverters.Add(new Rss20FeedHttpMessageConverter());
this._messageConverters.Add(new Atom10FeedHttpMessageConverter());
//this._messageConverters.Add(new JsonHttpMessageConverter());
#endif
}
#endregion
#region IRestOperations Membres
/// <summary>
/// Retrieve a representation by doing a GET on the specified URL.
/// The response (if any) is converted and returned.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The converted object</returns>
public T GetForObject<T>(string url, params string[] uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
@@ -198,6 +234,17 @@ namespace Spring.Http.Rest
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Retrieve a representation by doing a GET on the specified URL.
/// The response (if any) is converted and returned.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The converted object</returns>
public T GetForObject<T>(string url, IDictionary<string, string> uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
@@ -205,6 +252,13 @@ namespace Spring.Http.Rest
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Retrieve a representation by doing a GET on the specified URL.
/// The response (if any) is converted and returned.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <returns>The converted object</returns>
public T GetForObject<T>(Uri url) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
@@ -212,6 +266,17 @@ namespace Spring.Http.Rest
return this.Execute<T>(url, requestCallback, responseExtractor);
}
/// <summary>
/// Retrieve an entity by doing a GET on the specified URL.
/// The response is converted and stored in an <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> GetForMessage<T>(string url, params string[] uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
@@ -219,6 +284,17 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Retrieve an entity by doing a GET on the specified URL.
/// The response is converted and stored in an <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> GetForMessage<T>(string url, IDictionary<string, string> uriVariables) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
@@ -226,6 +302,13 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Retrieve an entity by doing a GET on the specified URL.
/// The response is converted and stored in an <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> GetForMessage<T>(Uri url) where T : class
{
AcceptHeaderRequestCallback requestCallback = new AcceptHeaderRequestCallback(HttpMethod.GET, typeof(T), this._messageConverters);
@@ -233,24 +316,64 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor);
}
/// <summary>
/// Retrieve all headers of the resource specified by the URI template.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>All HTTP headers of that resource</returns>
public WebHeaderCollection HeadForHeaders(string url, params string[] uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.HEAD);
return this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor, uriVariables);
}
/// <summary>
/// Retrieve all headers of the resource specified by the URI template.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>All HTTP headers of that resource</returns>
public WebHeaderCollection HeadForHeaders(string url, IDictionary<string, string> uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.HEAD);
return this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor, uriVariables);
}
/// <summary>
/// Retrieve all headers of the resource specified by the URI template.
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>All HTTP headers of that resource</returns>
public WebHeaderCollection HeadForHeaders(Uri url)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.HEAD);
return this.Execute<WebHeaderCollection>(url, requestCallback, this.headersExtractor);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the value of the 'Location' header.
/// This header typically indicates where the new resource is stored.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The value for the Location header.</returns>
public Uri PostForLocation(string url, object request, params string[] uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -261,6 +384,23 @@ namespace Spring.Http.Rest
return StringUtils.HasText(location) ? new Uri(location) : null;
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the value of the 'Location' header.
/// This header typically indicates where the new resource is stored.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The value for the Location header.</returns>
public Uri PostForLocation(string url, object request, IDictionary<string, string> uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -271,6 +411,17 @@ namespace Spring.Http.Rest
return StringUtils.HasText(location) ? new Uri(location) : null;
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the value of the 'Location' header.
/// This header typically indicates where the new resource is stored.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The value for the Location header.</returns>
public Uri PostForLocation(Uri url, object request)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -281,6 +432,23 @@ namespace Spring.Http.Rest
return StringUtils.HasText(location) ? new Uri(location) : null;
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the representation found in the response.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The converted object.</returns>
public T PostForObject<T>(string url, object request, params string[] uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -289,6 +457,23 @@ namespace Spring.Http.Rest
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the representation found in the response.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The converted object.</returns>
public T PostForObject<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -297,6 +482,17 @@ namespace Spring.Http.Rest
return this.Execute<T>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the representation found in the response.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The converted object.</returns>
public T PostForObject<T>(Uri url, object request) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -305,6 +501,23 @@ namespace Spring.Http.Rest
return this.Execute<T>(url, requestCallback, responseExtractor);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> PostForMessage<T>(string url, object request, params string[] uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -313,6 +526,23 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> PostForMessage<T>(string url, object request, IDictionary<string, string> uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -321,6 +551,17 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> PostForMessage<T>(Uri url, object request) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -329,6 +570,86 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message with no entity.</returns>
public HttpResponseMessage PostForMessage(string url, object request, params string[] uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, this._messageConverters);
HttpMessageResponseExtractor responseExtractor = new HttpMessageResponseExtractor();
return this.Execute<HttpResponseMessage>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message with no entity.</returns>
public HttpResponseMessage PostForMessage(string url, object request, IDictionary<string, string> uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, this._messageConverters);
HttpMessageResponseExtractor responseExtractor = new HttpMessageResponseExtractor();
return this.Execute<HttpResponseMessage>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Create a new resource by POSTing the given object to the URI template,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be POSTed, may be null.</param>
/// <returns>The HTTP response message with no entity.</returns>
public HttpResponseMessage PostForMessage(Uri url, object request)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
HttpMethod.POST, request, this._messageConverters);
HttpMessageResponseExtractor responseExtractor = new HttpMessageResponseExtractor();
return this.Execute<HttpResponseMessage>(url, requestCallback, responseExtractor);
}
/// <summary>
/// Create or update a resource by PUTting the given object to the URI.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given URI variables, if any.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be PUT, may be null.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
public void Put(string url, object request, params string[] uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -336,6 +657,20 @@ namespace Spring.Http.Rest
this.Execute<object>(url, requestCallback, null, uriVariables);
}
/// <summary>
/// Create or update a resource by PUTting the given object to the URI.
/// </summary>
/// <remarks>
/// <para>
/// URI Template variables are expanded using the given dictionary.
/// </para>
/// <para>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </para>
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be PUT, may be null.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
public void Put(string url, object request, IDictionary<string, string> uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -343,6 +678,14 @@ namespace Spring.Http.Rest
this.Execute<object>(url, requestCallback, null, uriVariables);
}
/// <summary>
/// Create or update a resource by PUTting the given object to the URI.
/// </summary>
/// <remarks>
/// The request parameter can be a <see cref="HttpRequestMessage"/> in order to add additional HTTP headers to the request.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="request">The Object to be PUT, may be null.</param>
public void Put(Uri url, object request)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(
@@ -350,24 +693,53 @@ namespace Spring.Http.Rest
this.Execute<object>(url, requestCallback, null);
}
/// <summary>
/// Delete the resources at the specified URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
public void Delete(string url, params string[] uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.DELETE);
this.Execute<object>(url, requestCallback, null, uriVariables);
}
/// <summary>
/// Delete the resources at the specified URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
public void Delete(string url, IDictionary<string, string> uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.DELETE);
this.Execute<object>(url, requestCallback, null, uriVariables);
}
/// <summary>
/// Delete the resources at the specified URI.
/// </summary>
/// <param name="url">The URL.</param>
public void Delete(Uri url)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.DELETE);
this.Execute<object>(url, requestCallback, null);
}
/// <summary>
/// Return the value of the Allow header for the given URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The value of the allow header.</returns>
public IList<HttpMethod> OptionsForAllow(string url, params string[] uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.OPTIONS);
@@ -378,6 +750,15 @@ namespace Spring.Http.Rest
return ParseAllowHeader(allow);
}
/// <summary>
/// Return the value of the Allow header for the given URI.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The value of the allow header.</returns>
public IList<HttpMethod> OptionsForAllow(string url, IDictionary<string, string> uriVariables)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.OPTIONS);
@@ -387,6 +768,11 @@ namespace Spring.Http.Rest
return ParseAllowHeader(allow);
}
/// <summary>
/// Return the value of the Allow header for the given URI.
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>The value of the allow header.</returns>
public IList<HttpMethod> OptionsForAllow(Uri url)
{
MethodRequestCallback requestCallback = new MethodRequestCallback(HttpMethod.OPTIONS);
@@ -396,6 +782,18 @@ namespace Spring.Http.Rest
return ParseAllowHeader(allow);
}
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> Exchange<T>(string url, HttpRequestMessage requestMessage, params string[] uriVariables) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, typeof(T), this._messageConverters);
@@ -403,6 +801,18 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message.</returns>
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);
@@ -410,6 +820,14 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response as <see cref="HttpResponseMessage{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <returns>The HTTP response message.</returns>
public HttpResponseMessage<T> Exchange<T>(Uri url, HttpRequestMessage requestMessage) where T : class
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, typeof(T), this._messageConverters);
@@ -417,6 +835,69 @@ namespace Spring.Http.Rest
return this.Execute<HttpResponseMessage<T>>(url, requestCallback, responseExtractor);
}
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>The HTTP response message with no entity.</returns>
public HttpResponseMessage Exchange(string url, HttpRequestMessage requestMessage, params string[] uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, this._messageConverters);
HttpMessageResponseExtractor responseExtractor = new HttpMessageResponseExtractor();
return this.Execute<HttpResponseMessage>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>The HTTP response message with no entity.</returns>
public HttpResponseMessage Exchange(string url, HttpRequestMessage requestMessage, IDictionary<string, string> uriVariables)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, this._messageConverters);
HttpMessageResponseExtractor responseExtractor = new HttpMessageResponseExtractor();
return this.Execute<HttpResponseMessage>(url, requestCallback, responseExtractor, uriVariables);
}
/// <summary>
/// Execute the HTTP request to the given URI template, writing the given request message to the request,
/// and returns the response with no entity as <see cref="HttpResponseMessage"/>.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="requestMessage">The HTTP request message to write to the request.</param>
/// <returns>The HTTP response message with no entity.</returns>
public HttpResponseMessage Exchange(Uri url, HttpRequestMessage requestMessage)
{
HttpMessageRequestCallback requestCallback = new HttpMessageRequestCallback(requestMessage, this._messageConverters);
HttpMessageResponseExtractor responseExtractor = new HttpMessageResponseExtractor();
return this.Execute<HttpResponseMessage>(url, requestCallback, responseExtractor);
}
/// <summary>
/// Execute the HTTP request to the given URI template, preparing the request with the
/// <see cref="IRequestCallback"/>, and reading the response with an <see cref="IResponseExtractor{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given URI variables, if any.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestCallback">Object that prepares the request.</param>
/// <param name="responseExtractor">Object that extracts the return value from the response.</param>
/// <param name="uriVariables">The variables to expand the template.</param>
/// <returns>An arbitrary object, as returned by the <see cref="IResponseExtractor{T}"/>.</returns>
public T Execute<T>(string url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor, params string[] uriVariables) where T : class
{
UriTemplate uriTemplate = new UriTemplate(url);
@@ -424,6 +905,19 @@ namespace Spring.Http.Rest
return this.DoExecute<T>(uri, requestCallback, responseExtractor);
}
/// <summary>
/// Execute the HTTP request to the given URI template, preparing the request with the
/// <see cref="IRequestCallback"/>, and reading the response with an <see cref="IResponseExtractor{T}"/>.
/// </summary>
/// <remarks>
/// URI Template variables are expanded using the given dictionary.
/// </remarks>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestCallback">Object that prepares the request.</param>
/// <param name="responseExtractor">Object that extracts the return value from the response.</param>
/// <param name="uriVariables">The dictionary containing variables for the URI template.</param>
/// <returns>An arbitrary object, as returned by the <see cref="IResponseExtractor{T}"/>.</returns>
public T Execute<T>(string url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor, IDictionary<string, string> uriVariables) where T : class
{
UriTemplate uriTemplate = new UriTemplate(url);
@@ -431,6 +925,15 @@ namespace Spring.Http.Rest
return this.DoExecute<T>(uri, requestCallback, responseExtractor);
}
/// <summary>
/// Execute the HTTP request to the given URI template, preparing the request with the
/// <see cref="IRequestCallback"/>, and reading the response with an <see cref="IResponseExtractor{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="requestCallback">Object that prepares the request.</param>
/// <param name="responseExtractor">Object that extracts the return value from the response.</param>
/// <returns>An arbitrary object, as returned by the <see cref="IResponseExtractor{T}"/>.</returns>
public T Execute<T>(Uri url, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor) where T : class
{
return this.DoExecute<T>(url, requestCallback, responseExtractor);
@@ -438,30 +941,30 @@ namespace Spring.Http.Rest
#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
/// <summary>
/// Execute the HTTP request to the given URI, preparing the request with the
/// <see cref="IRequestCallback"/>, and reading the response with an <see cref="IResponseExtractor{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the response value.</typeparam>
/// <param name="uri">The fully-expanded URI to connect to.</param>
/// <param name="requestCallback">Object that prepares the request.</param>
/// <param name="responseExtractor">Object that extracts the return value from the response.</param>
/// <returns>An arbitrary object, as returned by the <see cref="IResponseExtractor{T}"/>.</returns>
protected virtual T DoExecute<T>(Uri uri, IRequestCallback requestCallback, IResponseExtractor<T> responseExtractor) where T : class
{
HttpWebRequest request;
HttpWebResponse response = null;
Uri finalUri = url;
if (!url.IsAbsoluteUri)
Uri finalUri = uri;
if (!uri.IsAbsoluteUri)
{
if (this._baseAddress != null)
{
finalUri = new Uri(this._baseAddress, url);
finalUri = new Uri(this._baseAddress, uri);
}
else
{
throw new ArgumentException(String.Format("'{0}' is not an absolute URI", url), "url");
throw new ArgumentException(String.Format("'{0}' is not an absolute URI", uri), "uri");
}
}

View File

@@ -22,14 +22,17 @@ 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 prepares the request's accept headers.
*/
/// <summary>
/// Request callback implementation that prepares the request's accept headers.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class AcceptHeaderRequestCallback : MethodRequestCallback
{
#region Logging
@@ -38,9 +41,22 @@ namespace Spring.Http.Rest.Support
#endregion
/// <summary>
/// The expected response body type.
/// </summary>
protected Type responseType;
/// <summary>
/// The list of <see cref="IHttpMessageConverter"/> to use.
/// </summary>
protected IList<IHttpMessageConverter> messageConverters;
/// <summary>
/// Creates a new instance of <see cref="AcceptHeaderRequestCallback"/>.
/// </summary>
/// <param name="method">The HTTP method.</param>
/// <param name="responseType">The expected response body type.</param>
/// <param name="messageConverters">The list of <see cref="IHttpMessageConverter"/> to use.</param>
public AcceptHeaderRequestCallback(HttpMethod method, Type responseType, IList<IHttpMessageConverter> messageConverters) :
base(method)
{
@@ -48,6 +64,12 @@ namespace Spring.Http.Rest.Support
this.messageConverters = messageConverters;
}
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebRequest"/> to write data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="request">The active HTTP request.</param>
public override void DoWithRequest(HttpWebRequest request)
{
base.DoWithRequest(request);
@@ -61,7 +83,7 @@ namespace Spring.Http.Rest.Support
{
foreach (MediaType supportedMediaType in messageConverter.SupportedMediaTypes)
{
if (!String.IsNullOrEmpty(supportedMediaType.CharSet))
if (StringUtils.HasText(supportedMediaType.CharSet))
{
allSupportedMediaTypes.Add(new MediaType(
supportedMediaType.Type, supportedMediaType.Subtype));

View File

@@ -22,11 +22,19 @@ using System.Net;
namespace Spring.Http.Rest.Support
{
/**
* Response extractor that extracts the response {@link HttpHeaders}.
*/
/// <summary>
/// Response extractor that extracts the response HTTP headers.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class HeadersResponseExtractor : IResponseExtractor<WebHeaderCollection>
{
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebResponse"/> to extract data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="response">The active HTTP request.</param>
public WebHeaderCollection ExtractData(HttpWebResponse response)
{
return response.Headers;

View File

@@ -22,15 +22,16 @@ 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.
*/
/// <summary>
/// Request callback implementation that writes the given object to the request stream.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class HttpMessageRequestCallback : AcceptHeaderRequestCallback
{
#region Logging
@@ -41,11 +42,24 @@ namespace Spring.Http.Rest.Support
private HttpRequestMessage requestMessage;
/// <summary>
/// Creates a new instance of <see cref="HttpMessageRequestCallback"/>.
/// </summary>
/// <param name="method">The HTTP method.</param>
/// <param name="requestBody">The object to write to the request.</param>
/// <param name="messageConverters">The list of <see cref="IHttpMessageConverter"/> to use.</param>
public HttpMessageRequestCallback(HttpMethod method, object requestBody, IList<IHttpMessageConverter> messageConverters) :
this(method, requestBody, null, messageConverters)
{
}
/// <summary>
/// Creates a new instance of <see cref="HttpMessageRequestCallback"/>.
/// </summary>
/// <param name="method">The HTTP method.</param>
/// <param name="requestBody">The object to write to the request.</param>
/// <param name="responseType">The expected response body type.</param>
/// <param name="messageConverters">The list of <see cref="IHttpMessageConverter"/> to use.</param>
public HttpMessageRequestCallback(HttpMethod method, object requestBody, Type responseType, IList<IHttpMessageConverter> messageConverters) :
base(method, responseType, messageConverters)
{
@@ -60,17 +74,34 @@ namespace Spring.Http.Rest.Support
}
}
/// <summary>
/// Creates a new instance of <see cref="HttpMessageRequestCallback"/>.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="messageConverters">The list of <see cref="IHttpMessageConverter"/> to use.</param>
public HttpMessageRequestCallback(HttpRequestMessage requestMessage, IList<IHttpMessageConverter> messageConverters) :
this(requestMessage, null, messageConverters)
{
}
/// <summary>
/// Creates a new instance of <see cref="HttpMessageRequestCallback"/>.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="responseType">The expected response body type.</param>
/// <param name="messageConverters">The list of <see cref="IHttpMessageConverter"/> to use.</param>
public HttpMessageRequestCallback(HttpRequestMessage requestMessage, Type responseType, IList<IHttpMessageConverter> messageConverters) :
base(requestMessage.Method, responseType, messageConverters)
{
this.requestMessage = requestMessage;
}
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebRequest"/> to write data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="request">The active HTTP request.</param>
public override void DoWithRequest(HttpWebRequest request)
{
base.DoWithRequest(request);
@@ -78,18 +109,26 @@ namespace Spring.Http.Rest.Support
// headers
if (requestMessage.Headers.Count > 0)
{
request.Headers.Add(requestMessage.Headers);
foreach(string headerName in requestMessage.Headers)
{
// TODO : Check other special cases or create a HttpHeaders class
// Special cases
if (headerName == "Content-Type")
{
request.ContentType = requestMessage.Headers[HttpRequestHeader.ContentType];
}
else
{
request.Headers.Add(headerName, requestMessage.Headers[headerName]);
}
}
}
// 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]);
}
MediaType requestContentType = MediaType.ParseMediaType(requestMessage.Headers[HttpRequestHeader.ContentType]);
foreach (IHttpMessageConverter messageConverter in base.messageConverters)
{
if (messageConverter.CanWrite(requestBody.GetType(), requestContentType))

View File

@@ -19,37 +19,24 @@
#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
/// <summary>
/// Response extractor that extracts the HTTP response message with no body.
/// </summary>
/// <author>Bruno Baia</author>
public class HttpMessageResponseExtractor : IResponseExtractor<HttpResponseMessage>
{
private MessageConverterResponseExtractor<T> httpMessageConverterExtractor;
public HttpMessageResponseExtractor(IList<IHttpMessageConverter> messageConverters)
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebResponse"/> to extract data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="response">The active HTTP request.</param>
public HttpResponseMessage ExtractData(HttpWebResponse response)
{
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);
}
return new HttpResponseMessage(response.Headers, response.StatusCode, response.StatusDescription);
}
}
}

View File

@@ -0,0 +1,59 @@
#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.Http;
using Spring.Http.Converters;
namespace Spring.Http.Rest.Support
{
/// <summary>
/// Response extractor that extracts the HTTP response message with no body.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class HttpMessageResponseExtractor<T> : IResponseExtractor<HttpResponseMessage<T>> where T : class
{
private MessageConverterResponseExtractor<T> httpMessageConverterExtractor;
/// <summary>
/// Creates a new instance of the <see cref="HttpMessageResponseExtractor{T}"/> class.
/// </summary>
/// <param name="messageConverters">The list of <see cref="IHttpMessageConverter"/> to use.</param>
public HttpMessageResponseExtractor(IList<IHttpMessageConverter> messageConverters)
{
httpMessageConverterExtractor = new MessageConverterResponseExtractor<T>(messageConverters);
}
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebResponse"/> to extract data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="response">The active HTTP request.</param>
public HttpResponseMessage<T> ExtractData(HttpWebResponse response)
{
T body = httpMessageConverterExtractor.ExtractData(response);
return new HttpResponseMessage<T>(body, response.Headers, response.StatusCode, response.StatusDescription);
}
}
}

View File

@@ -28,14 +28,12 @@ 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
*/
/// <summary>
/// Response extractor that uses the given HTTP message converters to convert the response into a type.
/// </summary>
/// <typeparam name="T">The response body type.</typeparam>
/// <author>Arjen Poutsma</author>
/// <author>Bruno Baia (.NET)</author>
public class MessageConverterResponseExtractor<T> : IResponseExtractor<T> where T : class
{
#region Logging
@@ -46,25 +44,32 @@ namespace Spring.Http.Rest.Support
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.
*/
/// <summary>
/// Creates a new instance of the <see cref="MessageConverterResponseExtractor{T}"/> class.
/// </summary>
/// <param name="messageConverters">The list of <see cref="IHttpMessageConverter"/> to use.</param>
public MessageConverterResponseExtractor(IList<IHttpMessageConverter> messageConverters)
{
this.messageConverters = messageConverters;
}
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebResponse"/> to extract data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="response">The active HTTP request.</param>
public T ExtractData(HttpWebResponse response)
{
if (!StringUtils.HasText(response.Headers[HttpResponseHeader.ContentType]))
string contentType = response.Headers[HttpResponseHeader.ContentType];
if (!StringUtils.HasText(contentType))
{
throw new RestClientException("Could not extract response: no Content-Type found");
}
MediaType contentType = MediaType.ParseMediaType(response.Headers[HttpResponseHeader.ContentType]);
MediaType mediaType = MediaType.ParseMediaType(contentType);
foreach(IHttpMessageConverter messageConverter in messageConverters)
{
if (messageConverter.CanRead(typeof(T), contentType))
if (messageConverter.CanRead(typeof(T), mediaType))
{
#region Instrumentation
@@ -72,7 +77,7 @@ namespace Spring.Http.Rest.Support
{
LOG.Debug(String.Format(
"Reading [{0}] as '{1}' using [{2}]",
typeof(T).FullName, contentType, messageConverter));
typeof(T).FullName, mediaType, messageConverter));
}
#endregion
@@ -82,7 +87,7 @@ namespace Spring.Http.Rest.Support
}
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));
typeof(T).FullName, mediaType));
}
}
}

View File

@@ -25,9 +25,10 @@ using Spring.Http;
namespace Spring.Http.Rest.Support
{
/**
* Request callback implementation that sets the Http method.
*/
/// <summary>
/// Request callback implementation that sets the HTTP method.
/// </summary>
/// <author>Bruno Baia</author>
public class MethodRequestCallback : IRequestCallback
{
#region Logging
@@ -36,13 +37,26 @@ namespace Spring.Http.Rest.Support
#endregion
/// <summary>
/// The HTTP method.
/// </summary>
protected HttpMethod method;
/// <summary>
/// Creates a new instance of <see cref="MethodRequestCallback"/>.
/// </summary>
/// <param name="method">The HTTP method.</param>
public MethodRequestCallback(HttpMethod method)
{
this.method = method;
}
/// <summary>
/// Gets called by <see cref="RestTemplate"/> with an opened <see cref="HttpWebRequest"/> to write data.
/// Does not need to care about closing the request or about handling errors:
/// this will all be handled by the <see cref="RestTemplate"/> class.
/// </summary>
/// <param name="request">The active HTTP request.</param>
public virtual void DoWithRequest(HttpWebRequest request)
{
#region Instrumentation

View File

@@ -100,7 +100,7 @@
<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\Feed\Rss20FeedHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Json\JsonHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\AbstractXmlHttpMessageConverter.cs" />
<Compile Include="Http\Converters\Xml\XElementHttpMessageConverter.cs" />
@@ -109,9 +109,11 @@
<Compile Include="Http\Converters\Xml\DataContractHttpMessageConverter.cs" />
<Compile Include="Http\DefaultHttpWebRequestFactory.cs" />
<Compile Include="Http\HttpResponseMessage.cs" />
<Compile Include="Http\HttpResponseMessage`1.cs" />
<Compile Include="Http\IHttpWebRequestFactory.cs" />
<Compile Include="Http\HttpRequestMessage.cs" />
<Compile Include="Http\Rest\Support\AcceptHeaderRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HttpMessageResponseExtractor`1.cs" />
<Compile Include="Http\Rest\Support\MethodRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HttpMessageRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HeadersResponseExtractor.cs" />
@@ -128,14 +130,10 @@
<Compile Include="Http\Rest\RestClientException.cs" />
<Compile Include="Http\Rest\RestTemplate.cs" />
<Compile Include="Http\Converters\StringHttpMessageConverter.cs" />
<Compile Include="Util\AssertUtils.cs" />
<Compile Include="Util\StringUtils.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>

View File

@@ -111,7 +111,9 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Http\Converters\Feed\Rss20FeedHttpMessageConverter.cs" />
<Compile Include="Http\HttpResponseMessage.cs" />
<Compile Include="Http\HttpResponseMessage`1.cs" />
<Compile Include="Http\DefaultHttpWebRequestFactory.cs" />
<Compile Include="Http\Converters\AbstractHttpMessageConverter.cs">
<SubType>Code</SubType>
@@ -121,7 +123,6 @@
</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>
@@ -144,19 +145,18 @@
<Compile Include="Http\Rest\RestClientException.cs" />
<Compile Include="Http\Rest\RestTemplate.cs" />
<Compile Include="Http\Rest\Support\AcceptHeaderRequestCallback.cs" />
<Compile Include="Http\Rest\Support\HttpMessageResponseExtractor`1.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\AssertUtils.cs" />
<Compile Include="Util\StringUtils.cs">
<SubType>Code</SubType>
</Compile>
<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>

View File

@@ -0,0 +1,121 @@
#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.Globalization;
namespace Spring.Util
{
/// <summary>
/// Assertion utility methods that simplify things such as argument checks.
/// </summary>
/// <remarks>
/// <p>
/// Not intended to be used directly by applications.
/// </p>
/// </remarks>
/// <author>Aleksandar Seovic</author>
/// <author>Erich Eichinger</author>
internal sealed class AssertUtils
{
/// <summary>
/// Checks the value of the supplied <paramref name="argument"/> and throws an
/// <see cref="System.ArgumentNullException"/> if it is <see langword="null"/>.
/// </summary>
/// <param name="argument">The object to check.</param>
/// <param name="name">The argument name.</param>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="argument"/> is <see langword="null"/>.
/// </exception>
internal static void ArgumentNotNull(object argument, string name)
{
if (argument == null)
{
throw new ArgumentNullException (name,
String.Format(CultureInfo.InvariantCulture, "Argument '{0}' cannot be null.", name));
}
}
/// <summary>
/// Checks the value of the supplied <paramref name="argument"/> and throws an
/// <see cref="System.ArgumentNullException"/> if it is <see langword="null"/>.
/// </summary>
/// <param name="argument">The object to check.</param>
/// <param name="name">The argument name.</param>
/// <param name="message">
/// An arbitrary message that will be passed to any thrown
/// <see cref="System.ArgumentNullException"/>.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="argument"/> is <see langword="null"/>.
/// </exception>
internal static void ArgumentNotNull(object argument, string name, string message)
{
if (argument == null)
{
throw new ArgumentNullException(name, message);
}
}
/// <summary>
/// Checks the value of the supplied string <paramref name="argument"/> and throws an
/// <see cref="System.ArgumentNullException"/> if it is <see langword="null"/> or
/// contains only whitespace character(s).
/// </summary>
/// <param name="argument">The string to check.</param>
/// <param name="name">The argument name.</param>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="argument"/> is <see langword="null"/> or
/// contains only whitespace character(s).
/// </exception>
internal static void ArgumentHasText(string argument, string name)
{
if (!StringUtils.HasText(argument))
{
throw new ArgumentNullException(name,
String.Format (CultureInfo.InvariantCulture,
"Argument '{0}' cannot be null or resolve to an empty string : '{1}'.", name, argument));
}
}
/// <summary>
/// Checks the value of the supplied string <paramref name="argument"/> and throws an
/// <see cref="System.ArgumentNullException"/> if it is <see langword="null"/> or
/// contains only whitespace character(s).
/// </summary>
/// <param name="argument">The string to check.</param>
/// <param name="name">The argument name.</param>
/// <param name="message">
/// An arbitrary message that will be passed to any thrown
/// <see cref="System.ArgumentNullException"/>.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="argument"/> is <see langword="null"/> or
/// contains only whitespace character(s).
/// </exception>
internal static void ArgumentHasText(string argument, string name, string message)
{
if (!StringUtils.HasText(argument))
{
throw new ArgumentNullException(name, message);
}
}
}
}

View File

@@ -0,0 +1,103 @@
#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.Util
{
/// <summary>
/// Miscellaneous <see cref="System.String"/> utility methods.
/// </summary>
/// <remarks>
/// <p>
/// Mainly for internal use within the framework.
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Keith Donald</author>
/// <author>Aleksandar Seovic (.NET)</author>
/// <author>Mark Pollack (.NET)</author>
/// <author>Rick Evans (.NET)</author>
/// <author>Erich Eichinger (.NET)</author>
internal sealed class StringUtils
{
/// <summary>Checks if a string has length.</summary>
/// <param name="target">
/// The string to check, may be <see langword="null"/>.
/// </param>
/// <returns>
/// <see langword="true"/> if the string has length and is not
/// <see langword="null"/>.
/// </returns>
/// <example>
/// <code lang="C#">
/// StringUtils.HasLength(null) = false
/// StringUtils.HasLength("") = false
/// StringUtils.HasLength(" ") = true
/// StringUtils.HasLength("Hello") = true
/// </code>
/// </example>
internal static bool HasLength(string target)
{
return (target != null && target.Length > 0);
}
/// <summary>
/// Checks if a <see cref="System.String"/> has text.
/// </summary>
/// <remarks>
/// <p>
/// More specifically, returns <see langword="true"/> if the string is
/// not <see langword="null"/>, it's <see cref="String.Length"/> is >
/// zero <c>(0)</c>, and it has at least one non-whitespace character.
/// </p>
/// </remarks>
/// <param name="target">
/// The string to check, may be <see langword="null"/>.
/// </param>
/// <returns>
/// <see langword="true"/> if the <paramref name="target"/> is not
/// <see langword="null"/>,
/// <see cref="String.Length"/> > zero <c>(0)</c>, and does not consist
/// solely of whitespace.
/// </returns>
/// <example>
/// <code language="C#">
/// StringUtils.HasText(null) = false
/// StringUtils.HasText("") = false
/// StringUtils.HasText(" ") = false
/// StringUtils.HasText("12345") = true
/// StringUtils.HasText(" 12345 ") = true
/// </code>
/// </example>
internal static bool HasText(string target)
{
if (target == null)
{
return false;
}
else
{
return HasLength(target.Trim());
}
}
}
}

View File

@@ -28,16 +28,13 @@ 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>
*/
/// <summary>
/// Represents a URI template. An URI template is a URI-like String that contained variables
/// marked of in braces {}, which can be expanded to produce a URI.
/// </summary>
/// <author>Arjen Poutsma</author>
/// <author>Juergen Hoeller</author>
/// <author>Bruno Baia (.NET)</author>
public class UriTemplate
{
/** Captures URI template variable names. */
@@ -54,11 +51,18 @@ namespace Spring.Util
private string[] variableNames;
private Regex matchRegex;
/// <summary>
/// Gets the names of the variables in the template, in order.
/// </summary>
public string[] VariableNames
{
get { return this.variableNames; }
}
/// <summary>
/// Creates a new instance of <see cref="UriTemplate"/> with the given URI String.
/// </summary>
/// <param name="uriTemplate">The URI template string.</param>
public UriTemplate(string uriTemplate)
{
this.uriTemplate = uriTemplate;
@@ -67,6 +71,23 @@ namespace Spring.Util
this.matchRegex = parser.GetMatchRegex();
}
/// <summary>
/// Given the dictionary of variables, expands this template into a full URI.
/// The dictionary keys represent variable names, the dicitonary values variable values.
/// The order of variables is not significant.
/// </summary>
/// <example>
/// <code>
/// UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
/// IDictionary&lt;string, string&gt; uriVariables = new Dictionary&lt;String, String&gt;();
/// uriVariables.Add("booking", "42");
/// uriVariables.Add("hotel", "1");
/// Console.Out.WriteLine(template.Expand(uriVariables));
/// </code>
/// will print: <blockquote>http://example.com/hotels/1/bookings/42</blockquote>
/// </example>
/// <param name="uriVariables">The dictionary of URI variables.</param>
/// <returns>The expanded URI</returns>
public Uri Expand(IDictionary<string, string> uriVariables)
{
if (uriVariables.Count != this.variableNames.Length)
@@ -105,6 +126,19 @@ namespace Spring.Util
//return Expand(uriVariableValues);
}
/// <summary>
/// Given an array of variables, expands this template into a full URI.
/// The array represent variable values. The order of variables is significant.
/// </summary>
/// <example>
/// <code>
/// UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
/// Console.Out.WriteLine(template.Expand("1", "42"));
/// </code>
/// will print: <blockquote>http://example.com/hotels/1/bookings/42</blockquote>
/// </example>
/// <param name="uriVariableValues">The array of URI variables.</param>
/// <returns>The expanded URI</returns>
public Uri Expand(params string[] uriVariableValues)
{
if (uriVariableValues.Length != this.variableNames.Length)
@@ -123,11 +157,11 @@ namespace Spring.Util
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
*/
/// <summary>
/// Indicates whether the given URI matches this template.
/// </summary>
/// <param name="uri">The URI to match to.</param>
/// <returns><see langword="true"/> if it matches; otherwise <see langword="false"/></returns>
public bool Matches(string uri)
{
if (uri == null)
@@ -137,14 +171,19 @@ namespace Spring.Util
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
*/
/// <summary>
/// Match the given URI to a dictionary of variable values. Keys in the returned map are variable names,
/// values are variable values, as occurred in the given URI
/// </summary>
/// <example>
/// <code>
/// UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
/// Console.Out.WriteLine(template.Match("http://example.com/hotels/1/bookings/42"));
/// </code>
/// will print: <blockquote>{hotel=1, booking=42}</blockquote>
/// </example>
/// <param name="uri">The URI to match to.</param>
/// <returns>A dictionary of variable values.</returns>
public IDictionary<string, string> Match(string uri)
{
AssertUtils.ArgumentNotNull(uri, "uri");
@@ -158,6 +197,12 @@ namespace Spring.Util
return result;
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object."/>
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object."/>.
/// </returns>
public override string ToString()
{
return this.uriTemplate;

View File

@@ -49,13 +49,18 @@ namespace Spring.Http.Converters
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(byte[]), new MediaType("application", "octet-stream")));
Assert.IsTrue(converter.CanRead(typeof(byte[]), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanWrite(typeof(byte[]), MediaType.ALL));
Assert.IsFalse(converter.CanRead(typeof(string), new MediaType("application", "octet-stream")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanWrite(typeof(byte[]), new MediaType("application", "octet-stream")));
Assert.IsTrue(converter.CanRead(typeof(byte[]), new MediaType("application", "octet-stream")));
Assert.IsTrue(converter.CanRead(typeof(byte[]), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanWrite(typeof(byte[]), MediaType.ALL));
Assert.IsFalse(converter.CanRead(typeof(string), new MediaType("application", "octet-stream")));
}
[Test]
@@ -64,8 +69,8 @@ namespace Spring.Http.Converters
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();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(body));
Expect.Call<long>(webResponse.ContentLength).Return(2);
mocks.ReplayAll();
@@ -80,20 +85,23 @@ namespace Spring.Http.Converters
[Test]
public void Write()
{
MemoryStream requestStream = new MemoryStream();
byte[] body = new byte[] { 0x1, 0x2 };
HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
webRequest.Method = "POST";
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/octet-stream").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 2).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
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");
byte[] result = requestStream.ToArray();
Assert.AreEqual(body, result, "Invalid result");
mocks.VerifyAll();
}
}
}

View File

@@ -0,0 +1,132 @@
#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.IO;
using System.Net;
using System.Text;
using System.Globalization;
using System.ServiceModel.Syndication;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Converters.Feed
{
/// <summary>
/// Unit tests for the Atom10FeedHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class Atom10FeedHttpMessageConverterTests
{
private Atom10FeedHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new Atom10FeedHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("application", "atom+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationItem), new MediaType("application", "atom+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "xml")));
Assert.IsFalse(converter.CanRead(typeof(string), new MediaType("application", "atom+xml")));
Assert.IsFalse(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "plain")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("application", "atom+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationItem), new MediaType("application", "atom+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "xml")));
Assert.IsFalse(converter.CanRead(typeof(string), new MediaType("application", "atom+xml")));
Assert.IsFalse(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "plain")));
}
[Test]
public void Read()
{
DateTime now = DateTime.Now;
string body = String.Format("<feed xmlns=\"http://www.w3.org/2005/Atom\"><title type=\"text\">Test Feed</title><subtitle type=\"text\">This is a test feed</subtitle><id>Atom10FeedHttpMessageConverterTests.Write</id><rights type=\"text\">Copyright 2010</rights><updated>{0}</updated><author><name>Bruno Baïa</name><uri>http://www.springframework.net/bbaia</uri><email>bruno.baia@springframework.net</email></author><link rel=\"alternate\" href=\"http://www.springframework.net/Feed\" /></feed>",
now.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture));
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
mocks.ReplayAll();
SyndicationFeed result = converter.Read<SyndicationFeed>(webResponse);
Assert.IsNotNull(result, "Invalid result");
Assert.AreEqual("Atom10FeedHttpMessageConverterTests.Write", result.Id, "Invalid result");
Assert.AreEqual("Test Feed", result.Title.Text, "Invalid result");
Assert.AreEqual("This is a test feed", result.Description.Text, "Invalid result");
Assert.IsTrue(result.Links.Count == 1, "Invalid result");
Assert.AreEqual(new Uri("http://www.springframework.net/Feed"), result.Links[0].Uri, "Invalid result");
Assert.AreEqual("Copyright 2010", result.Copyright.Text, "Invalid result");
Assert.IsTrue(result.Authors.Count == 1, "Invalid result");
Assert.AreEqual("Bruno Baïa", result.Authors[0].Name, "Invalid result");
Assert.AreEqual("bruno.baia@springframework.net", result.Authors[0].Email, "Invalid result");
Assert.AreEqual("http://www.springframework.net/bbaia", result.Authors[0].Uri, "Invalid result");
mocks.VerifyAll();
}
[Test]
public void Write()
{
MemoryStream requestStream = new MemoryStream();
DateTime now = DateTime.Now;
string expectedBody = String.Format("<feed xmlns=\"http://www.w3.org/2005/Atom\"><title type=\"text\">Test Feed</title><subtitle type=\"text\">This is a test feed</subtitle><id>Atom10FeedHttpMessageConverterTests.Write</id><rights type=\"text\">Copyright 2010</rights><updated>{0}</updated><author><name>Bruno Baïa</name><uri>http://www.springframework.net/bbaia</uri><email>bruno.baia@springframework.net</email></author><link rel=\"alternate\" href=\"http://www.springframework.net/Feed\" /></feed>",
now.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture));
SyndicationFeed body = new SyndicationFeed("Test Feed", "This is a test feed", new Uri("http://www.springframework.net/Feed"), "Atom10FeedHttpMessageConverterTests.Write", now);
SyndicationPerson sp = new SyndicationPerson("bruno.baia@springframework.net", "Bruno Baïa", "http://www.springframework.net/bbaia");
body.Authors.Add(sp);
body.Copyright = new TextSyndicationContent("Copyright 2010");
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/atom+xml").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, null, webRequest);
byte[] result = requestStream.ToArray();
Assert.AreEqual(expectedBody, Encoding.UTF8.GetString(result), "Invalid result");
mocks.VerifyAll();
}
}
}
#endif

View File

@@ -0,0 +1,171 @@
#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 System.ServiceModel.Syndication;
using Spring.Http.Rest;
using NUnit.Framework;
namespace Spring.Http.Converters.Feed
{
/// <summary>
/// Integration tests for the SyndicationFeed based IHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class FeedHttpMessageConverterIntegrationTests
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(FeedHttpMessageConverterIntegrationTests));
#endregion
private WebServiceHost webServiceHost;
private string uri = "http://localhost:1337";
private RestTemplate template;
[SetUp]
public void SetUp()
{
template = new RestTemplate(uri);
template.MessageConverters = new List<IHttpMessageConverter>();
//template.MessageConverters.Add(new StringHttpMessageConverter()); // for debugging purpose
webServiceHost = new WebServiceHost(typeof(TestService), new Uri(uri));
webServiceHost.Open();
}
[TearDown]
public void TearDownClass()
{
webServiceHost.Close();
}
[Test]
public void Rss20GetForObject()
{
template.MessageConverters.Add(new Rss20FeedHttpMessageConverter());
SyndicationFeed result = template.GetForObject<SyndicationFeed>("feed");
Assert.IsNotNull(result, "Invalid content");
Assert.AreEqual("Test Feed", result.Title.Text, "Invalid content");
Assert.AreEqual("This is a test feed", result.Description.Text, "Invalid content");
}
[Test]
public void Rss20PostForMessage()
{
template.MessageConverters.Add(new Rss20FeedHttpMessageConverter());
SyndicationItem item = new SyndicationItem("Bruno's item", "Bruno's content", null);
HttpResponseMessage result = template.PostForMessage("feed/entry", item);
Assert.IsNull(result.Body, "Invalid content");
Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
Assert.AreEqual("Syndication item added with title 'Bruno's item'", result.StatusDescription, "Invalid status description");
}
[Test]
public void Atom10GetForObject()
{
template.MessageConverters.Add(new Atom10FeedHttpMessageConverter());
SyndicationFeed result = template.GetForObject<SyndicationFeed>("feed/?format=atom");
Assert.IsNotNull(result, "Invalid content");
Assert.AreEqual("Test Feed", result.Title.Text, "Invalid content");
Assert.AreEqual("This is a test feed", result.Description.Text, "Invalid content");
}
[Test]
public void Atom10PostForMessage()
{
template.MessageConverters.Add(new Atom10FeedHttpMessageConverter());
SyndicationItem item = new SyndicationItem("Bruno's item", "Bruno's content", null);
HttpResponseMessage result = template.PostForMessage("feed/entry", item);
Assert.IsNull(result.Body, "Invalid content");
Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
Assert.AreEqual("Syndication item added with title 'Bruno's item'", result.StatusDescription, "Invalid status description");
}
#region REST test service
[ServiceContract]
[ServiceKnownType(typeof(Atom10FeedFormatter))]
[ServiceKnownType(typeof(Rss20FeedFormatter))]
[ServiceKnownType(typeof(Atom10ItemFormatter))]
[ServiceKnownType(typeof(Rss20ItemFormatter))]
public class TestService
{
[WebGet(UriTemplate = "feed/", BodyStyle = WebMessageBodyStyle.Bare)]
public SyndicationFeedFormatter CreateFeed()
{
// Create a new Syndication Feed.
SyndicationFeed feed = new SyndicationFeed("Test Feed", "This is a test feed", null);
List<SyndicationItem> items = new List<SyndicationItem>();
// Create a new Syndication Item.
SyndicationItem item = new SyndicationItem("An item", "Item content", null);
items.Add(item);
feed.Items = items;
// Return ATOM or RSS based on uri
// rss -> http://localhost:1337/feed/
// atom -> http://localhost:1337/feed/?format=atom
string query = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
SyndicationFeedFormatter formatter = null;
if (query == "atom")
{
formatter = new Atom10FeedFormatter(feed);
}
else
{
formatter = new Rss20FeedFormatter(feed);
}
return formatter;
}
[WebInvoke(UriTemplate = "feed/entry")]
public void AddEntry(SyndicationItemFormatter item)
{
WebOperationContext context = WebOperationContext.Current;
// Add entry
// ..
context.OutgoingResponse.StatusCode = HttpStatusCode.Created;
context.OutgoingResponse.StatusDescription = String.Format("Syndication item added with title '{0}'", item.Item.Title.Text);
}
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,131 @@
#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.IO;
using System.Net;
using System.Text;
using System.Globalization;
using System.ServiceModel.Syndication;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Converters.Feed
{
/// <summary>
/// Unit tests for the Rss20FeedHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class Rss20FeedHttpMessageConverterTests
{
private Rss20FeedHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new Rss20FeedHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("application", "rss+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationItem), new MediaType("application", "rss+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "xml")));
Assert.IsFalse(converter.CanRead(typeof(string), new MediaType("application", "rss+xml")));
Assert.IsFalse(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "plain")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("application", "rss+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationItem), new MediaType("application", "rss+xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "xml")));
Assert.IsFalse(converter.CanRead(typeof(string), new MediaType("application", "rss+xml")));
Assert.IsFalse(converter.CanRead(typeof(SyndicationFeed), new MediaType("text", "plain")));
}
[Test]
public void Read()
{
DateTime now = DateTime.Now;
string body = String.Format("<rss xmlns:a10=\"http://www.w3.org/2005/Atom\" version=\"2.0\"><channel><title>Test Feed</title><link>http://www.springframework.net/Feed</link><description>This is a test feed</description><copyright>Copyright 2010</copyright><managingEditor>bruno.baia@springframework.net</managingEditor><lastBuildDate>{0}</lastBuildDate><a10:id>Atom10FeedHttpMessageConverterTests.Write</a10:id></channel></rss>",
now.ToString("ddd, dd MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture).Remove(29, 1));
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
mocks.ReplayAll();
SyndicationFeed result = converter.Read<SyndicationFeed>(webResponse);
Assert.IsNotNull(result, "Invalid result");
Assert.AreEqual("Atom10FeedHttpMessageConverterTests.Write", result.Id, "Invalid result");
Assert.AreEqual("Test Feed", result.Title.Text, "Invalid result");
Assert.AreEqual("This is a test feed", result.Description.Text, "Invalid result");
Assert.IsTrue(result.Links.Count == 1, "Invalid result");
Assert.AreEqual(new Uri("http://www.springframework.net/Feed"), result.Links[0].Uri, "Invalid result");
Assert.AreEqual("Copyright 2010", result.Copyright.Text, "Invalid result");
Assert.IsTrue(result.Authors.Count == 1, "Invalid result");
Assert.AreEqual("bruno.baia@springframework.net", result.Authors[0].Email, "Invalid result");
mocks.VerifyAll();
}
[Test]
public void Write()
{
MemoryStream requestStream = new MemoryStream();
DateTime now = DateTime.Now;
string expectedBody = String.Format("<rss xmlns:a10=\"http://www.w3.org/2005/Atom\" version=\"2.0\"><channel><title>Test Feed</title><link>http://www.springframework.net/Feed</link><description>This is a test feed</description><copyright>Copyright 2010</copyright><managingEditor>bruno.baia@springframework.net</managingEditor><lastBuildDate>{0}</lastBuildDate><a10:id>Atom10FeedHttpMessageConverterTests.Write</a10:id></channel></rss>",
now.ToString("ddd, dd MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture).Remove(29, 1));
SyndicationFeed body = new SyndicationFeed("Test Feed", "This is a test feed", new Uri("http://www.springframework.net/Feed"), "Atom10FeedHttpMessageConverterTests.Write", now);
SyndicationPerson sp = new SyndicationPerson("bruno.baia@springframework.net", "Bruno Baïa", "http://www.springframework.net/bbaia");
body.Authors.Add(sp);
body.Copyright = new TextSyndicationContent("Copyright 2010");
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/rss+xml").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, null, webRequest);
byte[] result = requestStream.ToArray();
Assert.AreEqual(expectedBody, Encoding.UTF8.GetString(result), "Invalid result");
mocks.VerifyAll();
}
}
}
#endif

View File

@@ -0,0 +1,171 @@
#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 System.Runtime.Serialization;
using Spring.Http.Rest;
using NUnit.Framework;
namespace Spring.Http.Converters.Json
{
/// <summary>
/// Integration tests for the JsonHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class JsonHttpMessageConverterIntegrationTests
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(JsonHttpMessageConverterIntegrationTests));
#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", "json");
webServiceHost = new WebServiceHost(typeof(TestService), new Uri(uri));
webServiceHost.Open();
}
[TearDown]
public void TearDownClass()
{
webServiceHost.Close();
}
[Test]
public void GetForObject()
{
template.MessageConverters.Add(new JsonHttpMessageConverter());
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 PostForMessage()
{
template.MessageConverters.Add(new JsonHttpMessageConverter());
User user = new User() { Name = "Lisa Baia" };
HttpResponseMessage result = template.PostForMessage("user", user);
Assert.IsNull(result.Body, "Invalid content");
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");
}
#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;
context.OutgoingResponse.Format = WebMessageFormat.Json;
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 void Create(User user)
{
WebOperationContext context = WebOperationContext.Current;
context.OutgoingResponse.Format = WebMessageFormat.Json;
UriTemplateMatch match = context.IncomingRequest.UriTemplateMatch;
UriTemplate template = new UriTemplate("/user/{id}");
MediaType mediaType = MediaType.ParseMediaType(context.IncomingRequest.ContentType);
if (!String.IsNullOrEmpty(user.ID))
{
context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' already exists", user.ID);
return;
}
user.ID = (users.Count + 1).ToString(); // generate new ID
users.Add(user);
Uri uri = template.BindByPosition(match.BaseUri, user.ID);
context.OutgoingResponse.SetStatusAsCreated(uri);
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' created with '{1}'", user.ID, user.Name);
}
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,125 @@
#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.IO;
using System.Net;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Converters.Json
{
/// <summary>
/// Unit tests for the JsonHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class JsonHttpMessageConverterTests
{
private JsonHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new JsonHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(CustomClass), new MediaType("application", "json")));
Assert.IsFalse(converter.CanRead(typeof(CustomClass), new MediaType("text", "xml")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanRead(typeof(CustomClass), new MediaType("application", "json")));
Assert.IsFalse(converter.CanRead(typeof(CustomClass), new MediaType("text", "xml")));
}
[Test]
public void Read()
{
string body = "{\"ID\":\"1\",\"Name\":\"Bruno Baïa\"}";
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
mocks.ReplayAll();
CustomClass result = converter.Read<CustomClass>(webResponse);
Assert.IsNotNull(result, "Invalid result");
Assert.AreEqual("1", result.ID, "Invalid result");
Assert.AreEqual("Bruno Baïa", result.Name, "Invalid result");
mocks.VerifyAll();
}
[Test]
public void Write()
{
MemoryStream requestStream = new MemoryStream();
string expectedBody = "{\"ID\":\"1\",\"Name\":\"Bruno Baïa\"}";
CustomClass body = new CustomClass("1", "Bruno Baïa");
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/json").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, null, webRequest);
byte[] result = requestStream.ToArray();
Assert.AreEqual(expectedBody, Encoding.UTF8.GetString(result), "Invalid result");
mocks.VerifyAll();
}
#region Test classes
public class CustomClass
{
public string ID { get; set; }
public string Name { get; set; }
public CustomClass()
{
}
public CustomClass(string id, string name)
{
this.ID = id;
this.Name = name;
}
}
#endregion
}
}
#endif

View File

@@ -50,13 +50,18 @@ namespace Spring.Http.Converters
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(string), new MediaType("text", "plain")));
Assert.IsTrue(converter.CanWrite(typeof(string), MediaType.ALL));
Assert.IsTrue(converter.CanRead(typeof(string), new MediaType("application", "xml")));
Assert.IsFalse(converter.CanRead(typeof(int[]), new MediaType("text", "plain")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanWrite(typeof(string), new MediaType("text", "plain")));
Assert.IsTrue(converter.CanRead(typeof(string), new MediaType("text", "plain")));
Assert.IsTrue(converter.CanWrite(typeof(string), MediaType.ALL));
Assert.IsTrue(converter.CanRead(typeof(string), new MediaType("application", "xml")));
Assert.IsFalse(converter.CanRead(typeof(int[]), new MediaType("text", "plain")));
}
[Test]
@@ -65,7 +70,7 @@ namespace Spring.Http.Converters
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<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
Expect.Call<string>(webResponse.CharacterSet).Return("utf-8").Repeat.Twice();
mocks.ReplayAll();
@@ -79,48 +84,50 @@ namespace Spring.Http.Converters
[Test]
public void WriteDefaultCharset()
{
string body = "H\u00e9llo W\u00f6rld";
MemoryStream requestStream = new MemoryStream();
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";
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "text/plain;charset=ISO-8859-1").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, null, webRequest);
using (Stream postStream = webRequest.GetRequestStream())
{
//Assert.AreEqual(body.Length, postStream.Length, "Invalid result");
}
byte[] result = requestStream.ToArray();
Assert.AreEqual(body, charSetEncoding.GetString(result), "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");
mocks.VerifyAll();
}
[Test]
public void WriteUTF8()
{
string body = "H\u00e9llo W\u00f6rld";
MemoryStream requestStream = new MemoryStream();
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";
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = mediaType.ToString()).PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, mediaType, webRequest);
using (Stream postStream = webRequest.GetRequestStream())
{
//Assert.AreEqual(body.Length, postStream.Length, "Invalid result");
}
byte[] result = requestStream.ToArray();
Assert.AreEqual(body, charSetEncoding.GetString(result), "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");
mocks.VerifyAll();
}
}
}

View File

@@ -1,328 +0,0 @@
#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

@@ -19,15 +19,14 @@
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Runtime.Serialization;
using System.Collections.Generic;
using NUnit.Framework;
using Rhino.Mocks;
using System.Xml;
using System.Runtime.Serialization;
namespace Spring.Http.Converters.Xml
{
@@ -51,66 +50,102 @@ namespace Spring.Http.Converters.Xml
[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
Assert.IsTrue(converter.CanRead(typeof(DataContractClass), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(DataContractClass), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(DataContractClass), new MediaType("application", "soap+xml"))); // application/*+xml
Assert.IsFalse(converter.CanRead(typeof(DataContractClass), new MediaType("text", "plain")));
Assert.IsFalse(converter.CanRead(typeof(NonDataContractClass), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(CollectionDataContractClass), new MediaType("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
Assert.IsTrue(converter.CanWrite(typeof(DataContractClass), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanWrite(typeof(DataContractClass), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(DataContractClass), new MediaType("application", "soap+xml"))); // application/*+xml
Assert.IsFalse(converter.CanRead(typeof(DataContractClass), new MediaType("text", "plain")));
Assert.IsFalse(converter.CanRead(typeof(NonDataContractClass), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(CollectionDataContractClass), new MediaType("application", "xml")));
}
//[Test]
//public void Read()
//{
// string body = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><TestElement testAttribute=\"value\" />";
[Test]
public void Read()
{
string body = @"<?xml version='1.0' encoding='UTF-8' ?>
<DataContractHttpMessageConverterTests.DataContractClass xmlns='http://schemas.datacontract.org/2004/07/Spring.Http.Converters.Xml' xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>
<ID>1</ID><Name>Bruno Baïa</Name>
</DataContractHttpMessageConverterTests.DataContractClass>";
// HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
// Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body))).Repeat.Once();
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
// mocks.ReplayAll();
mocks.ReplayAll();
// XmlDocument result = converter.Read<XmlDocument>(webResponse);
// Assert.IsNotNull(result, "Invalid result");
DataContractClass result = converter.Read<DataContractClass>(webResponse);
Assert.IsNotNull(result, "Invalid result");
Assert.AreEqual("1", result.ID, "Invalid result");
Assert.AreEqual("Bruno Baïa", result.Name, "Invalid result");
// mocks.VerifyAll();
//}
mocks.VerifyAll();
}
//[Test]
//public void Write()
//{
// XmlDocument body = new XmlDocument();
// body.CreateElement("TestElement");
[Test]
public void Write()
{
MemoryStream requestStream = new MemoryStream();
// HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
// webRequest.Method = "POST";
string expectedBody = "<DataContractHttpMessageConverterTests.DataContractClass xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/Spring.Http.Converters.Xml\"><ID>1</ID><Name>Bruno Baïa</Name></DataContractHttpMessageConverterTests.DataContractClass>";
DataContractClass body = new DataContractClass("1", "Bruno Baïa");
// converter.Write(body, null, webRequest);
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/xml").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
// Assert.AreEqual(new MediaType("application", "xml"), MediaType.ParseMediaType(webRequest.ContentType), "Invalid content-type");
mocks.ReplayAll();
// using (Stream postStream = webRequest.GetRequestStream())
// {
// using (StreamReader reader = new StreamReader(postStream))
// {
// string result = reader.ReadToEnd();
// Assert.AreEqual(result.Length, webRequest.ContentLength, "Invalid content-length");
// }
// }
//}
converter.Write(body, null, webRequest);
requestStream.Position = 0;
using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8))
{
string result = reader.ReadToEnd();
Assert.AreEqual(expectedBody, result, "Invalid result");
}
mocks.VerifyAll();
}
#region Test classes
[DataContract]
public class CustomClass
public class DataContractClass
{
[DataMember]
public string ID { get; set; }
[DataMember]
public string Name { get; set; }
public DataContractClass(string id, string name)
{
this.ID = id;
this.Name = name;
}
}
[CollectionDataContract]
public class CollectionDataContractClass : List<string>
{
public CollectionDataContractClass()
: base()
{
}
}
public class NonDataContractClass
{
}
#endregion

View File

@@ -0,0 +1,124 @@
#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.IO;
using System.Net;
using System.Text;
using System.Linq;
using System.Xml.Linq;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Converters.Xml
{
/// <summary>
/// Unit tests for the XElementHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class XElementHttpMessageConverterTests
{
private XElementHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new XElementHttpMessageConverter();
}
[Test]
public void CanRead()
{
Assert.IsTrue(converter.CanRead(typeof(XElement), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanRead(typeof(XElement), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(XElement), new MediaType("application", "soap+xml"))); // application/*+xml
Assert.IsFalse(converter.CanRead(typeof(XElement), new MediaType("text", "plain")));
Assert.IsFalse(converter.CanRead(typeof(String), new MediaType("application", "xml")));
}
[Test]
public void CanWrite()
{
Assert.IsTrue(converter.CanWrite(typeof(XElement), new MediaType("application", "xml")));
Assert.IsTrue(converter.CanWrite(typeof(XElement), new MediaType("text", "xml")));
Assert.IsTrue(converter.CanRead(typeof(XElement), new MediaType("application", "soap+xml"))); // application/*+xml
Assert.IsFalse(converter.CanRead(typeof(XElement), new MediaType("text", "plain")));
Assert.IsFalse(converter.CanRead(typeof(String), new MediaType("application", "xml")));
}
[Test]
public void Read()
{
string body = "<?xml version='1.0' encoding='UTF-8' ?><Root><TestElement testAttribute='value'/><TestElement testAttribute='novalue'/></Root>";
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
mocks.ReplayAll();
XElement result = converter.Read<XElement>(webResponse);
Assert.IsNotNull(result, "Invalid result");
//XElement xResult = result.Elements()
// .Where(x => x.Name == "TestElement" && x.Attribute("testAttribute").Value == "value")
// .Single();
XElement xResult = (from el in result.Elements()
where el.Name == "TestElement" && el.Attribute("testAttribute").Value == "value"
select el)
.Single();
Assert.IsNotNull(xResult, "Invalid result");
mocks.VerifyAll();
}
[Test]
public void Write()
{
MemoryStream requestStream = new MemoryStream();
XElement body = new XElement("Root",
new XElement("TestElement", 1),
new XElement("TestElement", 2));
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/xml").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, null, webRequest);
requestStream.Position = 0;
using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8))
{
string result = reader.ReadToEnd();
Assert.AreEqual(body.ToString(SaveOptions.DisableFormatting), result, "Invalid result");
}
mocks.VerifyAll();
}
}
}
#endif

View File

@@ -22,10 +22,10 @@ using System;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using NUnit.Framework;
using Rhino.Mocks;
using System.Xml;
namespace Spring.Http.Converters.Xml
{
@@ -52,6 +52,8 @@ namespace Spring.Http.Converters.Xml
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
Assert.IsFalse(converter.CanRead(typeof(XmlDocument), new MediaType("text", "plain")));
Assert.IsFalse(converter.CanRead(typeof(String), new MediaType("application", "xml")));
}
[Test]
@@ -60,20 +62,27 @@ namespace Spring.Http.Converters.Xml
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
Assert.IsFalse(converter.CanRead(typeof(XmlDocument), new MediaType("text", "plain")));
Assert.IsFalse(converter.CanRead(typeof(String), new MediaType("application", "xml")));
}
[Test]
public void Read()
{
string body = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><TestElement testAttribute=\"value\" />";
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();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
mocks.ReplayAll();
XmlDocument result = converter.Read<XmlDocument>(webResponse);
Assert.IsNotNull(result, "Invalid result");
XmlNode xmlNodeResult = result.SelectSingleNode("//TestElement");
Assert.IsNotNull(xmlNodeResult, "Invalid result");
Assert.AreEqual("TestElement", xmlNodeResult.LocalName, "Invalid result");
Assert.IsNotNull(xmlNodeResult.Attributes["testAttribute"], "Invalid result");
Assert.AreEqual("value", xmlNodeResult.Attributes["testAttribute"].Value, "Invalid result");
mocks.VerifyAll();
}
@@ -81,25 +90,28 @@ namespace Spring.Http.Converters.Xml
[Test]
public void Write()
{
XmlDocument body = new XmlDocument();
body.CreateElement("TestElement");
MemoryStream requestStream = new MemoryStream();
HttpWebRequest webRequest = WebRequest.Create("http://localhost") as HttpWebRequest;
webRequest.Method = "POST";
XmlDocument body = new XmlDocument();
body.LoadXml("<?xml version='1.0' encoding='UTF-8' ?><TestElement testAttribute='value' />");
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/xml").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, null, webRequest);
Assert.AreEqual(new MediaType("application", "xml"), MediaType.ParseMediaType(webRequest.ContentType), "Invalid content-type");
using (Stream postStream = webRequest.GetRequestStream())
requestStream.Position = 0;
using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8))
{
using (StreamReader reader = new StreamReader(postStream))
{
string result = reader.ReadToEnd();
Assert.AreEqual(result.Length, webRequest.ContentLength, "Invalid content-length");
}
string result = reader.ReadToEnd();
Assert.AreEqual(body.OuterXml, result, "Invalid result");
}
mocks.VerifyAll();
}
}
}

View File

@@ -0,0 +1,240 @@
#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.Xml.Linq;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Runtime.Serialization;
using Spring.Http.Rest;
using NUnit.Framework;
namespace Spring.Http.Converters.Xml
{
/// <summary>
/// Integration tests for the Xml based IHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class XmlHttpMessageConverterIntegrationTests
{
#region Logging
private static readonly Common.Logging.ILog LOG = Common.Logging.LogManager.GetLogger(typeof(XmlHttpMessageConverterIntegrationTests));
#endregion
private WebServiceHost webServiceHost;
private string uri = "http://localhost:1337";
private RestTemplate template;
[SetUp]
public void SetUp()
{
template = new RestTemplate(uri);
template.MessageConverters = new List<IHttpMessageConverter>();
//template.MessageConverters.Add(new StringHttpMessageConverter()); // for debugging purpose
webServiceHost = new WebServiceHost(typeof(TestService), new Uri(uri));
webServiceHost.Open();
}
[TearDown]
public void TearDownClass()
{
webServiceHost.Close();
}
[Test]
public void DataContractGetForObject()
{
template.MessageConverters.Add(new DataContractHttpMessageConverter());
User result = template.GetForObject<User>("user/dc/{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 DataContractPostForMessage()
{
template.MessageConverters.Add(new DataContractHttpMessageConverter());
User user = new User() { Name = "Lisa Baia" };
HttpResponseMessage result = template.PostForMessage("user/dc", user);
Assert.IsNull(result.Body, "Invalid content");
Assert.AreEqual(new Uri(new Uri(uri), "/user/dc/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");
}
[Test]
public void XElementGetForObject()
{
template.MessageConverters.Add(new XElementHttpMessageConverter());
XElement result = template.GetForObject<XElement>("user/xml/{id}", "1");
Assert.IsNotNull(result, "Invalid content");
Assert.AreEqual("1", result.Element("ID").Value, "Invalid content");
Assert.AreEqual("Bruno Baïa", result.Element("Name").Value, "Invalid content");
}
[Test]
public void XElementPostForMessage()
{
template.MessageConverters.Add(new XElementHttpMessageConverter());
XElement user = new XElement("User",
new XElement("Name", "Lisa Baia"));
HttpResponseMessage result = template.PostForMessage("user/xml", user);
Assert.IsNull(result.Body, "Invalid content");
Assert.AreEqual(new Uri(new Uri(uri), "/user/xml/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");
}
#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/dc/{id}")]
public User GetUserDataContract(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/dc", Method = "POST")]
public void CreateDataContract(User user)
{
WebOperationContext context = WebOperationContext.Current;
UriTemplateMatch match = context.IncomingRequest.UriTemplateMatch;
UriTemplate template = new UriTemplate("/user/dc/{id}");
MediaType mediaType = MediaType.ParseMediaType(context.IncomingRequest.ContentType);
if (!String.IsNullOrEmpty(user.ID))
{
context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' already exists", user.ID);
return;
}
user.ID = (users.Count + 1).ToString(); // generate new ID
users.Add(user);
Uri uri = template.BindByPosition(match.BaseUri, user.ID);
context.OutgoingResponse.SetStatusAsCreated(uri);
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' created with '{1}'", user.ID, user.Name);
}
[WebGet(UriTemplate = "user/xml/{id}")]
public XElement GetUserXElement(string id)
{
WebOperationContext context = WebOperationContext.Current;
foreach (User user in this.users)
{
if (user.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase))
{
return new XElement("User",
new XElement("ID", user.ID),
new XElement("Name", user.Name));
}
}
context.OutgoingResponse.SetStatusAsNotFound(String.Format("User with id '{0}' not found", id));
return null;
}
[WebInvoke(UriTemplate = "user/xml", Method = "POST")]
public void CreateXElement(XElement user)
{
WebOperationContext context = WebOperationContext.Current;
UriTemplateMatch match = context.IncomingRequest.UriTemplateMatch;
UriTemplate template = new UriTemplate("/user/xml/{id}");
MediaType mediaType = MediaType.ParseMediaType(context.IncomingRequest.ContentType);
if (user.Element("ID") != null && !String.IsNullOrEmpty(user.Element("ID").Value))
{
context.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' already exists", user.Element("ID"));
return;
}
User newUser = new User();
newUser.ID = (users.Count + 1).ToString(); // generate new ID
newUser.Name = user.Element("Name").Value;
users.Add(newUser);
Uri uri = template.BindByPosition(match.BaseUri, newUser.ID);
context.OutgoingResponse.SetStatusAsCreated(uri);
context.OutgoingResponse.StatusDescription = String.Format("User id '{0}' created with '{1}'", newUser.ID, newUser.Name);
}
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,137 @@
#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.IO;
using System.Net;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
namespace Spring.Http.Converters.Xml
{
/// <summary>
/// Unit tests for the XmlSerializableHttpMessageConverter class.
/// </summary>
/// <author>Bruno Baia</author>
[TestFixture]
public class XmlSerializableHttpMessageConverterTests
{
private XmlSerializableHttpMessageConverter converter;
private MockRepository mocks;
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
converter = new XmlSerializableHttpMessageConverter();
}
[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
Assert.IsFalse(converter.CanRead(typeof(CustomClass), new MediaType("text", "plain")));
}
[Test]
public void CanWrite()
{
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
Assert.IsFalse(converter.CanRead(typeof(CustomClass), new MediaType("text", "plain")));
}
[Test]
public void Read()
{
string body = @"<?xml version='1.0' encoding='utf-8'?>
<CustomClass xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
<ID>1</ID>
<Name>Bruno Baïa</Name>
</CustomClass>";
HttpWebResponse webResponse = mocks.CreateMock<HttpWebResponse>();
Expect.Call<Stream>(webResponse.GetResponseStream()).Return(new MemoryStream(Encoding.UTF8.GetBytes(body)));
mocks.ReplayAll();
CustomClass result = converter.Read<CustomClass>(webResponse);
Assert.IsNotNull(result, "Invalid result");
Assert.AreEqual("1", result.ID, "Invalid result");
Assert.AreEqual("Bruno Baïa", result.Name, "Invalid result");
mocks.VerifyAll();
}
[Test]
public void Write()
{
MemoryStream requestStream = new MemoryStream();
string expectedBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?><CustomClass xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><ID>1</ID><Name>Bruno Baïa</Name></CustomClass>";
CustomClass body = new CustomClass("1", "Bruno Baïa");
HttpWebRequest webRequest = mocks.CreateMock<HttpWebRequest>();
Expect.Call(webRequest.ContentType = "application/xml").PropertyBehavior();
Expect.Call(webRequest.ContentLength = 1337).PropertyBehavior();
Expect.Call<Stream>(webRequest.GetRequestStream()).Return(requestStream);
mocks.ReplayAll();
converter.Write(body, null, webRequest);
requestStream.Position = 0;
using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8))
{
string result = reader.ReadToEnd();
Assert.AreEqual(expectedBody, result, "Invalid result");
}
mocks.VerifyAll();
}
#region Test classes
public class CustomClass
{
public string ID { get; set; }
public string Name { get; set; }
public CustomClass()
{
}
public CustomClass(string id, string name)
{
this.ID = id;
this.Name = name;
}
}
#endregion
}
}
#endif

View File

@@ -222,7 +222,7 @@ namespace Spring.Http.Rest
[Test]
public void ExchangePut()
{
HttpResponseMessage<string> result = template.Exchange<string>(
HttpResponseMessage result = template.Exchange(
"user/1", new HttpRequestMessage("Bruno Baia", HttpMethod.PUT));
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Invalid status code");
@@ -458,7 +458,6 @@ namespace Spring.Http.Rest
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")]

View File

@@ -158,6 +158,7 @@ namespace Spring.Http.Rest
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
ExpectGetResponse();
//Expect.Call(errorHandler.hasError(response)).andReturn(false);
//Expect.Call<string>(response.ContentType).Return(textPlain.ToString()).Repeat.AtLeastOnce();
WebHeaderCollection responseHeaders = new WebHeaderCollection();
responseHeaders[HttpResponseHeader.ContentType] = textPlain.ToString();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders).Repeat.Any();
@@ -181,7 +182,7 @@ namespace Spring.Http.Rest
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<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();
@@ -279,6 +280,7 @@ namespace Spring.Http.Rest
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(request.ContentType = contentType.ToString());
Expect.Call<bool>(converter.CanWrite(typeof(string), contentType)).Return(true);
WebHeaderCollection requestHeaders = new WebHeaderCollection();
Expect.Call<WebHeaderCollection>(request.Headers).Return(requestHeaders).Repeat.Any();
@@ -301,7 +303,7 @@ namespace Spring.Http.Rest
}
[Test]
public void PostForLocationEntityCustomHeader()
public void PostForLocationMessageCustomHeader()
{
string helloWorld = "Hello World";
Expect.Call<HttpWebRequest>(requestFactory.CreateRequest(new Uri("http://example.com"))).Return(request);
@@ -399,7 +401,7 @@ namespace Spring.Http.Rest
}
[Test]
public void PostForEntity()
public void PostForMessage()
{
Expect.Call<bool>(converter.CanRead(typeof(Version), null)).Return(true);
MediaType textPlain = new MediaType("text", "plain");
@@ -434,6 +436,31 @@ namespace Spring.Http.Rest
Assert.AreEqual("OK", result.StatusDescription, "Invalid status description");
}
[Test]
public void PostForMessageNoBody()
{
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();
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();
Expect.Call<WebHeaderCollection>(response.Headers).Return(responseHeaders);
Expect.Call<HttpStatusCode>(response.StatusCode).Return(HttpStatusCode.Created);
Expect.Call<string>(response.StatusDescription).Return("CREATED");
mocks.ReplayAll();
HttpResponseMessage result = template.PostForMessage("http://example.com", helloWorld);
Assert.IsNull(result.Body, "Invalid POST result");
Assert.AreEqual(HttpStatusCode.Created, result.StatusCode, "Invalid status code");
Assert.AreEqual("CREATED", result.StatusDescription, "Invalid status description");
}
[Test]
public void PostForObjectNull()
{

View File

@@ -87,28 +87,43 @@
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Runtime.Serialization">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="System.ServiceModel">
<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="AssemblyInfo.cs" />
<Compile Include="Http\Converters\ByteArrayHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Feed\Atom10FeedHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Feed\FeedHttpMessageConverterIntegrationTests.cs" />
<Compile Include="Http\Converters\Feed\Rss20FeedHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Json\JsonHttpMessageConverterIntegrationTests.cs" />
<Compile Include="Http\Converters\Json\JsonHttpMessageConverterTests.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\XElementHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\XmlDocumentHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\XmlHttpMessageConverterIntegrationTests.cs" />
<Compile Include="Http\Converters\Xml\XmlSerializableHttpMessageConverterTests.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>

View File

@@ -108,19 +108,30 @@
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.ServiceModel.Web" />
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Http\Converters\ByteArrayHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Feed\Atom10FeedHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Feed\FeedHttpMessageConverterIntegrationTests.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Http\Converters\Feed\Rss20FeedHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Json\JsonHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Json\JsonHttpMessageConverterIntegrationTests.cs" />
<Compile Include="Http\Converters\StringHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\XmlHttpMessageConverterIntegrationTests.cs" />
<Compile Include="Http\Converters\Xml\XElementHttpMessageConverterTests.cs" />
<Compile Include="Http\Converters\Xml\XmlSerializableHttpMessageConverterTests.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" />
@@ -128,10 +139,6 @@
<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>