REST client API: Polishing (SPRNET-1345)
This commit is contained in:
@@ -102,7 +102,6 @@ namespace Spring.Http.Client
|
||||
/// </summary>
|
||||
public Action<Stream> Body
|
||||
{
|
||||
get { return this.body; }
|
||||
set { this.body = value; }
|
||||
}
|
||||
|
||||
|
||||
@@ -20,55 +20,150 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Spring.Util;
|
||||
|
||||
namespace Spring.Http.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="IHttpMessageConverter"/> that can write files.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A mapping between file extension and mime types is used to determine the Content-Type of written files.
|
||||
/// If no Content-Type is available, 'application/octet-stream' is used.
|
||||
/// </remarks>
|
||||
/// <author>Bruno Baia</author>
|
||||
public class FileInfoHttpMessageConverter : AbstractHttpMessageConverter
|
||||
public class FileInfoHttpMessageConverter : IHttpMessageConverter
|
||||
{
|
||||
// Pre-defined mapping between file extension and mime types
|
||||
private static IDictionary<string, string> defaultMimeMapping;
|
||||
|
||||
private IList<MediaType> _supportedMediaTypes;
|
||||
private IDictionary<string, string> _mimeMapping;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ByteArrayHttpMessageConverter"/>
|
||||
/// with 'text/plain; charset=ISO-8859-1', and '*/*' media types.
|
||||
/// Gets or sets the mapping between file extension and mime types.
|
||||
/// </summary>
|
||||
public FileInfoHttpMessageConverter() :
|
||||
base(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)
|
||||
public IDictionary<string, string> MimeMapping
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._mimeMapping == null)
|
||||
{
|
||||
this._mimeMapping = new Dictionary<string, string>(defaultMimeMapping);
|
||||
}
|
||||
return _mimeMapping;
|
||||
}
|
||||
set { _mimeMapping = value; }
|
||||
}
|
||||
|
||||
static FileInfoHttpMessageConverter()
|
||||
{
|
||||
defaultMimeMapping = new Dictionary<string, string>(9, StringComparer.OrdinalIgnoreCase);
|
||||
defaultMimeMapping.Add(".bmp", "image/bmp");
|
||||
defaultMimeMapping.Add(".gif", "image/gif");
|
||||
defaultMimeMapping.Add(".jpg", "image/jpeg");
|
||||
defaultMimeMapping.Add(".jpeg", "image/jpeg");
|
||||
defaultMimeMapping.Add(".pdf", "application/pdf");
|
||||
defaultMimeMapping.Add(".png", "image/png");
|
||||
defaultMimeMapping.Add(".tif", "image/tiff");
|
||||
defaultMimeMapping.Add(".txt", "text/plain");
|
||||
defaultMimeMapping.Add(".zip", "application/x-zip-compressed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the given class is supported by this converter.
|
||||
/// Creates a new instance of the <see cref="FileInfoHttpMessageConverter"/>
|
||||
/// with 'application/octet-stream', and '*/*' media types.
|
||||
/// </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)
|
||||
public FileInfoHttpMessageConverter()
|
||||
{
|
||||
this._supportedMediaTypes = new List<MediaType>();
|
||||
this._supportedMediaTypes.Add(MediaType.APPLICATION_OCTET_STREAM);
|
||||
this._supportedMediaTypes.Add(MediaType.ALL);
|
||||
}
|
||||
|
||||
#region IHttpMessageConverter Membres
|
||||
|
||||
/// <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>
|
||||
public bool CanRead(Type type, MediaType mediaType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public bool CanWrite(Type type, MediaType mediaType)
|
||||
{
|
||||
return type.Equals(typeof(FileInfo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract template method that reads the actualy object. Invoked from <see cref="M:Read"/>.
|
||||
/// Gets the list of <see cref="MediaType"/> objects supported by this converter.
|
||||
/// </summary>
|
||||
public IList<MediaType> SupportedMediaTypes
|
||||
{
|
||||
get { return this._supportedMediaTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an object of the given type form the given HTTP message, and returns it.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object to return.</typeparam>
|
||||
/// <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="message">The HTTP message to read from.</param>
|
||||
/// <returns>The converted object.</returns>
|
||||
/// <exception cref="HttpMessageNotReadableException">In case of conversion errors</exception>
|
||||
protected override T ReadInternal<T>(IHttpInputMessage message)
|
||||
public T Read<T>(IHttpInputMessage message) where T : class
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract template method that writes the actual body. Invoked from <see cref="M:Write"/>.
|
||||
/// Write an given object to the given HTTP message.
|
||||
/// </summary>
|
||||
/// <param name="content">The object to write to the HTTP message.</param>
|
||||
/// <param name="content">
|
||||
/// The object to write to the HTTP message. 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="contentType">
|
||||
/// 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="message">The HTTP message to write to.</param>
|
||||
/// <exception cref="HttpMessageNotWritableException">In case of conversion errors</exception>
|
||||
protected override void WriteInternal(object content, IHttpOutputMessage message)
|
||||
public void Write(object content, MediaType contentType, IHttpOutputMessage message)
|
||||
{
|
||||
// Get the content type
|
||||
HttpHeaders headers = message.Headers;
|
||||
if (headers.ContentType == null)
|
||||
{
|
||||
if (contentType == null || contentType.IsWildcardType || contentType.IsWildcardSubtype)
|
||||
{
|
||||
contentType = GetContentType(content as FileInfo);
|
||||
}
|
||||
if (contentType != null)
|
||||
{
|
||||
headers.ContentType = contentType;
|
||||
}
|
||||
}
|
||||
|
||||
// Write to the message stream
|
||||
message.Body = delegate(Stream stream)
|
||||
{
|
||||
@@ -78,5 +173,23 @@ namespace Spring.Http.Converters
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private MediaType GetContentType(FileInfo file)
|
||||
{
|
||||
IDictionary<string, string> mimeMapping =
|
||||
(this._mimeMapping == null) ? defaultMimeMapping : this._mimeMapping;
|
||||
|
||||
string mimeType;
|
||||
if (mimeMapping.TryGetValue(file.Extension, out mimeType))
|
||||
{
|
||||
return MediaType.Parse(mimeType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,6 @@ namespace Spring.Http
|
||||
/// <summary>
|
||||
/// Sets the delegate that writes the body message as a stream.
|
||||
/// </summary>
|
||||
Action<Stream> Body { get; set; }
|
||||
Action<Stream> Body { set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1843,11 +1843,11 @@ namespace Spring.Http.Rest
|
||||
{
|
||||
if (this._errorHandler.HasError(response))
|
||||
{
|
||||
this.HandleResponseError(uri, method, response);
|
||||
HandleResponseError(uri, method, response, this._errorHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogResponseStatus(uri, method, response);
|
||||
LogResponseStatus(uri, method, response);
|
||||
}
|
||||
|
||||
if (responseExtractor != null)
|
||||
@@ -1883,7 +1883,7 @@ namespace Spring.Http.Rest
|
||||
{
|
||||
IClientHttpRequest request = this._requestFactory.CreateRequest(uri, method);
|
||||
|
||||
ExecuteState<T> state = new ExecuteState<T>(uri, method, responseExtractor, methodCompleted);
|
||||
ExecuteState<T> state = new ExecuteState<T>(uri, method, responseExtractor, this._errorHandler, methodCompleted);
|
||||
|
||||
if (requestCallback != null)
|
||||
{
|
||||
@@ -1900,7 +1900,7 @@ namespace Spring.Http.Rest
|
||||
}
|
||||
}
|
||||
|
||||
private void ResponseReceivedCallback<T>(ExecuteCompletedEventArgs responseReceived) where T : class
|
||||
private static void ResponseReceivedCallback<T>(ExecuteCompletedEventArgs responseReceived) where T : class
|
||||
{
|
||||
ExecuteState<T> state = (ExecuteState<T>)responseReceived.UserState;
|
||||
if (responseReceived.Error == null)
|
||||
@@ -1917,13 +1917,13 @@ namespace Spring.Http.Rest
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
if (this._errorHandler.HasError(response))
|
||||
if (state.ResponseErrorHandler.HasError(response))
|
||||
{
|
||||
this.HandleResponseError(state.Uri, state.Method, response);
|
||||
HandleResponseError(state.Uri, state.Method, response, state.ResponseErrorHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogResponseStatus(state.Uri, state.Method, response);
|
||||
LogResponseStatus(state.Uri, state.Method, response);
|
||||
}
|
||||
|
||||
if (state.ResponseExtractor != null)
|
||||
@@ -1948,20 +1948,23 @@ namespace Spring.Http.Rest
|
||||
}
|
||||
}
|
||||
|
||||
private class ExecuteState<T> where T : class
|
||||
private sealed class ExecuteState<T> where T : class
|
||||
{
|
||||
public Uri Uri;
|
||||
public HttpMethod Method;
|
||||
public IResponseExtractor<T> ResponseExtractor;
|
||||
public IResponseErrorHandler ResponseErrorHandler;
|
||||
public Action<MethodCompletedEventArgs<T>> MethodCompleted;
|
||||
|
||||
public ExecuteState(Uri uri, HttpMethod method,
|
||||
IResponseExtractor<T> responseExtractor,
|
||||
IResponseErrorHandler responseErrorHandler,
|
||||
Action<MethodCompletedEventArgs<T>> methodCompleted)
|
||||
{
|
||||
this.Uri = uri;
|
||||
this.Method = method;
|
||||
this.ResponseExtractor = responseExtractor;
|
||||
this.ResponseErrorHandler = responseErrorHandler;
|
||||
this.MethodCompleted = methodCompleted;
|
||||
}
|
||||
}
|
||||
@@ -2030,7 +2033,7 @@ namespace Spring.Http.Rest
|
||||
|
||||
#endregion
|
||||
|
||||
private void LogResponseStatus(Uri uri, HttpMethod method, IClientHttpResponse response)
|
||||
private static void LogResponseStatus(Uri uri, HttpMethod method, IClientHttpResponse response)
|
||||
{
|
||||
#region Instrumentation
|
||||
#if !SILVERLIGHT
|
||||
@@ -2044,7 +2047,8 @@ namespace Spring.Http.Rest
|
||||
#endregion
|
||||
}
|
||||
|
||||
private void HandleResponseError(Uri uri, HttpMethod method, IClientHttpResponse response)
|
||||
private static void HandleResponseError(Uri uri, HttpMethod method, IClientHttpResponse response,
|
||||
IResponseErrorHandler errorHandler)
|
||||
{
|
||||
#region Instrumentation
|
||||
#if !SILVERLIGHT
|
||||
@@ -2057,7 +2061,7 @@ namespace Spring.Http.Rest
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
this._errorHandler.HandleError(response);
|
||||
errorHandler.HandleError(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user