Add JSON formatter support
This commit is contained in:
parent
99f612e5cb
commit
bd2e434da9
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.CoreServices
|
||||
|
|
@ -82,5 +84,23 @@ namespace Microsoft.AspNet.CoreServices
|
|||
{
|
||||
return constructor.IsPublic && constructor.GetParameters().Length != 0;
|
||||
}
|
||||
|
||||
public static Func<TBase> Create<TBase>(Type instanceType) where TBase : class
|
||||
{
|
||||
Contract.Assert(instanceType != null);
|
||||
NewExpression newInstanceExpression = Expression.New(instanceType);
|
||||
return Expression.Lambda<Func<TBase>>(newInstanceExpression).Compile();
|
||||
}
|
||||
|
||||
public static Func<TInstance> Create<TInstance>() where TInstance : class
|
||||
{
|
||||
return Create<TInstance>(typeof(TInstance));
|
||||
}
|
||||
|
||||
public static Func<object> Create(Type instanceType)
|
||||
{
|
||||
Contract.Assert(instanceType != null);
|
||||
return Create<object>(instanceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,50 @@
|
|||
using System.Net.Http;
|
||||
using Microsoft.AspNet.CoreServices;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class ActionResultFactory : IActionResultFactory
|
||||
{
|
||||
public IActionResult CreateActionResult(object actionReturnValue)
|
||||
public IActionResult CreateActionResult(Type declaredReturnType, object actionReturnValue, RequestContext requestContext)
|
||||
{
|
||||
// optimize common path
|
||||
IActionResult actionResult = actionReturnValue as IActionResult;
|
||||
|
||||
if (actionResult != null)
|
||||
{
|
||||
return actionResult;
|
||||
}
|
||||
|
||||
bool isDeclaredTypeActionResult = typeof(IActionResult).IsAssignableFrom(declaredReturnType);
|
||||
bool isDeclaredTypeResponseMessage = typeof(HttpResponseMessage).IsAssignableFrom(declaredReturnType);
|
||||
|
||||
if ((isDeclaredTypeActionResult || isDeclaredTypeResponseMessage) && actionReturnValue == null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot return null from an action method declaring IActionResult or HttpResponseMessage");
|
||||
}
|
||||
|
||||
if (declaredReturnType == null)
|
||||
{
|
||||
throw new InvalidOperationException("Declared type must be passed");
|
||||
}
|
||||
|
||||
if (declaredReturnType.IsGenericParameter)
|
||||
{
|
||||
// This can happen if somebody declares an action method as:
|
||||
// public T Get<T>() { }
|
||||
throw new InvalidOperationException("HttpActionDescriptor_NoConverterForGenericParamterTypeExists");
|
||||
}
|
||||
|
||||
if (declaredReturnType.IsAssignableFrom(typeof(void)))
|
||||
{
|
||||
return new NoContentResult();
|
||||
}
|
||||
|
||||
var responseMessage = actionReturnValue as HttpResponseMessage;
|
||||
if (responseMessage != null)
|
||||
{
|
||||
|
|
@ -17,19 +55,18 @@ namespace Microsoft.AspNet.Mvc
|
|||
{
|
||||
return new ContentResult
|
||||
{
|
||||
Content = (string)actionReturnValue
|
||||
ContentType = "text/plain",
|
||||
Content = (string)actionReturnValue,
|
||||
};
|
||||
}
|
||||
|
||||
// all other object types are treated as an http response message action result
|
||||
var content = new ObjectContent(actionReturnValue.GetType(),
|
||||
actionReturnValue,
|
||||
new JsonMediaTypeFormatter());
|
||||
// TODO: this needs to get injected
|
||||
IOwinContentNegotiator contentNegotiator = new DefaultContentNegotiator();
|
||||
|
||||
return new HttpResponseMessageActionResult(new HttpResponseMessage
|
||||
{
|
||||
Content = content
|
||||
});
|
||||
// TODO: inject the formatters
|
||||
IEnumerable<MediaTypeFormatter> formatters = requestContext.Formatters;
|
||||
|
||||
return new NegotiatedContentResult(HttpStatusCode.OK, declaredReturnType, actionReturnValue, contentNegotiator, requestContext.HttpContext, formatters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Owin;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http.Properties;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>Represents an action result that performs content negotiation.</summary>
|
||||
/// <typeparam name="T">The type of content in the entity body.</typeparam>
|
||||
internal class NegotiatedContentResult : IActionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NegotiatedContentResult{T}"/> class with the values provided.
|
||||
/// </summary>
|
||||
/// <param name="statusCode">The HTTP status code for the response message.</param>
|
||||
/// <param name="content">The content value to negotiate and format in the entity body.</param>
|
||||
/// <param name="contentNegotiator">The content negotiator to handle content negotiation.</param>
|
||||
/// <param name="request">The request message which led to this result.</param>
|
||||
/// <param name="formatters">The formatters to use to negotiate and format the content.</param>
|
||||
public NegotiatedContentResult(HttpStatusCode statusCode,
|
||||
Type declaredType,
|
||||
object content,
|
||||
IOwinContentNegotiator contentNegotiator,
|
||||
IOwinContext owinContext,
|
||||
IEnumerable<MediaTypeFormatter> formatters)
|
||||
{
|
||||
Contract.Assert(content != null);
|
||||
Contract.Assert(declaredType != null);
|
||||
Contract.Assert(owinContext != null);
|
||||
Contract.Assert(formatters != null);
|
||||
|
||||
StatusCode = statusCode;
|
||||
DeclaredType = declaredType;
|
||||
Content = content;
|
||||
CurrentOwinContext = owinContext;
|
||||
Formatters = formatters;
|
||||
ContentNegotiator = contentNegotiator;
|
||||
}
|
||||
|
||||
/// <summary>Gets the HTTP status code for the response message.</summary>
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
|
||||
public Type DeclaredType { get; private set; }
|
||||
|
||||
/// <summary>Gets the content value to negotiate and format in the entity body.</summary>
|
||||
public object Content { get; private set; }
|
||||
|
||||
/// <summary>Gets the content negotiator to handle content negotiation.</summary>
|
||||
public IOwinContentNegotiator ContentNegotiator { get; private set; }
|
||||
|
||||
/// <summary>Gets the request message which led to this result.</summary>
|
||||
public IOwinContext CurrentOwinContext { get; private set; }
|
||||
|
||||
/// <summary>Gets the formatters to use to negotiate and format the content.</summary>
|
||||
public IEnumerable<MediaTypeFormatter> Formatters { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task ExecuteResultAsync(RequestContext context)
|
||||
{
|
||||
// Run content negotiation.
|
||||
ContentNegotiationResult result = ContentNegotiator.Negotiate(DeclaredType, CurrentOwinContext, Formatters);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
// A null result from content negotiation indicates that the response should be a 406.
|
||||
CurrentOwinContext.Response.StatusCode = (int)HttpStatusCode.NotAcceptable;
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
IOwinResponse response = CurrentOwinContext.Response;
|
||||
response.StatusCode = (int)StatusCode;
|
||||
Contract.Assert(result.Formatter != null);
|
||||
|
||||
var objectContent = new ObjectContent(DeclaredType, Content, result.Formatter, result.MediaType);
|
||||
|
||||
// Copy non-content headers
|
||||
IDictionary<string, string[]> responseHeaders = response.Headers;
|
||||
foreach (KeyValuePair<string, string[]> header in response.Headers)
|
||||
{
|
||||
responseHeaders[header.Key] = header.Value.AsArray();
|
||||
}
|
||||
|
||||
// Copy content headers
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> contentHeader in objectContent.Headers)
|
||||
{
|
||||
responseHeaders[contentHeader.Key] = contentHeader.Value.AsArray();
|
||||
}
|
||||
|
||||
return objectContent.CopyToAsync(response.Body);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
object actionReturnValue = method.Invoke(controller, null);
|
||||
|
||||
// If it's not already an IActionResult then call into the factory
|
||||
var actionResult = actionReturnValue as IActionResult ?? _actionResultFactory.CreateActionResult(actionReturnValue);
|
||||
var actionResult = actionReturnValue as IActionResult ?? _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _requestContext);
|
||||
|
||||
return actionResult.ExecuteResultAsync(_requestContext);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
|
||||
using Microsoft.Owin;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public interface IActionResultFactory
|
||||
{
|
||||
IActionResult CreateActionResult(object actionReturnValue);
|
||||
IActionResult CreateActionResult(Type declaredReturnType, object actionReturnValue, RequestContext requestContext);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,8 +53,17 @@
|
|||
<Compile Include="ActionInvokerProvider.cs" />
|
||||
<Compile Include="ActionResultFactory.cs" />
|
||||
<Compile Include="ActionResultHelper.cs" />
|
||||
<Compile Include="ContentResult.cs" />
|
||||
<Compile Include="ActionResults\NoContentResult.cs" />
|
||||
<Compile Include="ActionResults\NegotiatedContentResult.cs" />
|
||||
<Compile Include="ActionResults\ContentResult.cs" />
|
||||
<Compile Include="ActionResults\ObjectContent.cs" />
|
||||
<Compile Include="ControllerBasedActionDescriptor.cs" />
|
||||
<Compile Include="DefaultContentNegotiator.cs" />
|
||||
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
||||
<Compile Include="Formatters\JQeryMvcForUrlEncodedFormatter.cs" />
|
||||
<Compile Include="FormattingUtilities.cs" />
|
||||
<Compile Include="Extensions\TypeExtensions.cs" />
|
||||
<Compile Include="IOwinContentNegotiator.cs" />
|
||||
<Compile Include="RequestContext.cs" />
|
||||
<Compile Include="EmptyResult.cs" />
|
||||
<Compile Include="HttpResponseMessageActionResult.cs" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.CoreServices;
|
||||
using System.Net.Http.Formatting;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
|
|
@ -18,6 +19,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
callback(typeof(IActionInvokerFactory), typeof(ActionInvokerFactory));
|
||||
callback(typeof(IActionResultHelper), typeof(ActionResultHelper));
|
||||
callback(typeof(IActionResultFactory), typeof(ActionResultFactory));
|
||||
callback(typeof(IContentNegotiator), typeof(DefaultContentNegotiator));
|
||||
|
||||
// TODO: Should be many
|
||||
callback(typeof(IActionDescriptorProvider), typeof(ActionDescriptorProvider));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Mvc.Routing;
|
||||
using Microsoft.Owin;
|
||||
using System.Net.Http.Formatting;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
|
|
@ -20,10 +21,23 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
HttpContext = context;
|
||||
RouteData = routeData;
|
||||
|
||||
// todo: inject
|
||||
InjectFormatters();
|
||||
}
|
||||
|
||||
private void InjectFormatters()
|
||||
{
|
||||
Formatters = new MediaTypeFormatterCollection();
|
||||
Formatters.Add(new JsonMediaTypeFormatter());
|
||||
//Formatters.Add(new XmlMediaTypeFormatter());
|
||||
//Formatters.Add(new JQueryMvcFormUrlEncodedFormatter());
|
||||
}
|
||||
|
||||
public virtual IRouteData RouteData { get; set; }
|
||||
|
||||
public virtual IOwinContext HttpContext { get; set; }
|
||||
|
||||
public virtual MediaTypeFormatterCollection Formatters { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,9 +37,15 @@ namespace MvcSample
|
|||
return responseMessage;
|
||||
}
|
||||
|
||||
public IActionResult MyView()
|
||||
public User User()
|
||||
{
|
||||
return Result.View();
|
||||
User user = new User()
|
||||
{
|
||||
Name = "My name",
|
||||
Address = "My address"
|
||||
};
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace MvcSample
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Address { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +67,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Home2Controller.cs" />
|
||||
<Compile Include="HomeController.cs" />
|
||||
<Compile Include="Models\Class1.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Startup.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in New Issue