Adding functional tests and attribute route tests.
Incorporating PR coments.
This commit is contained in:
parent
d91b7776b3
commit
c8b911b596
15
Mvc.sln
15
Mvc.sln
|
|
@ -130,6 +130,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValidationWebSite", "test\W
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Xml.Test", "test\Microsoft.AspNet.Mvc.Xml.Test\Microsoft.AspNet.Mvc.Xml.Test.kproj", "{22019146-BDFA-442E-8C8E-345FB9644578}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Xml.Test", "test\Microsoft.AspNet.Mvc.Xml.Test\Microsoft.AspNet.Mvc.Xml.Test.kproj", "{22019146-BDFA-442E-8C8E-345FB9644578}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "FormatFilterWebSite", "test\WebSites\FormatFilterWebSite\FormatFilterWebSite.kproj", "{AC9BE567-540E-4C70-90C2-AAF021307A80}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -740,6 +742,18 @@ Global
|
||||||
{22019146-BDFA-442E-8C8E-345FB9644578}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{22019146-BDFA-442E-8C8E-345FB9644578}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.ActiveCfg = Release|Any CPU
|
{22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.Build.0 = Release|Any CPU
|
{22019146-BDFA-442E-8C8E-345FB9644578}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -803,5 +817,6 @@ Global
|
||||||
{C3123A70-41C4-4122-AD1C-D35DF8958DD7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{C3123A70-41C4-4122-AD1C-D35DF8958DD7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{87AB84B2-22C1-43C6-BB8A-1D327B446FB0} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{22019146-BDFA-442E-8C8E-345FB9644578} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{22019146-BDFA-442E-8C8E-345FB9644578} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
|
{AC9BE567-540E-4C70-90C2-AAF021307A80} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace MvcSample.Web.Controllers
|
||||||
|
{
|
||||||
|
[FormatFilter]
|
||||||
|
public class FormatFilterController : Controller
|
||||||
|
{
|
||||||
|
public Product GetProduct(int id)
|
||||||
|
{
|
||||||
|
return new Product() { SampleInt = id };
|
||||||
|
}
|
||||||
|
|
||||||
|
[Produces("application/custom", "application/json", "text/json")]
|
||||||
|
public Product ProducesMethod(int id)
|
||||||
|
{
|
||||||
|
return new Product() { SampleInt = id }; ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MvcSample.Web
|
||||||
|
{
|
||||||
|
public class Product
|
||||||
|
{
|
||||||
|
public int SampleInt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,6 @@ using Microsoft.Framework.ConfigurationModel;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using MvcSample.Web.Filters;
|
using MvcSample.Web.Filters;
|
||||||
using MvcSample.Web.Services;
|
using MvcSample.Web.Services;
|
||||||
using Microsoft.AspNet.Mvc.Core.Filters;
|
|
||||||
|
|
||||||
#if ASPNET50
|
#if ASPNET50
|
||||||
using Autofac;
|
using Autofac;
|
||||||
|
|
@ -64,13 +63,9 @@ namespace MvcSample.Web
|
||||||
services.Configure<MvcOptions>(options =>
|
services.Configure<MvcOptions>(options =>
|
||||||
{
|
{
|
||||||
options.Filters.Add(typeof(PassThroughAttribute), order: 17);
|
options.Filters.Add(typeof(PassThroughAttribute), order: 17);
|
||||||
<<<<<<< HEAD
|
|
||||||
|
|
||||||
options.AddXmlDataContractSerializerFormatter();
|
options.AddXmlDataContractSerializerFormatter();
|
||||||
=======
|
var formatFilter = new FormatFilterAttribute();
|
||||||
var formatFilter = new FormatFilter();
|
|
||||||
options.Filters.Add(formatFilter);
|
options.Filters.Add(formatFilter);
|
||||||
>>>>>>> This is MVC part of feature URL Extensions. It does following:
|
|
||||||
});
|
});
|
||||||
services.Configure<RazorViewEngineOptions>(options =>
|
services.Configure<RazorViewEngineOptions>(options =>
|
||||||
{
|
{
|
||||||
|
|
@ -113,7 +108,7 @@ namespace MvcSample.Web
|
||||||
options.Filters.Add(typeof(PassThroughAttribute), order: 17);
|
options.Filters.Add(typeof(PassThroughAttribute), order: 17);
|
||||||
options.AddXmlDataContractSerializerFormatter();
|
options.AddXmlDataContractSerializerFormatter();
|
||||||
|
|
||||||
var formatFilter = new FormatFilter();
|
var formatFilter = new FormatFilterAttribute();
|
||||||
options.Filters.Add(formatFilter);
|
options.Filters.Add(formatFilter);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -121,10 +116,8 @@ namespace MvcSample.Web
|
||||||
|
|
||||||
app.UseMvc(routes =>
|
app.UseMvc(routes =>
|
||||||
{
|
{
|
||||||
|
routes.MapRoute("FormatRoute", "{controller}/{action}/{id}.{format?}");
|
||||||
routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action}");
|
routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action}");
|
||||||
|
|
||||||
routes.MapRoute("formatRoute", "{controller}/{action}/{format}");
|
|
||||||
|
|
||||||
routes.MapRoute(
|
routes.MapRoute(
|
||||||
"controllerActionRoute",
|
"controllerActionRoute",
|
||||||
"{controller}/{action}",
|
"{controller}/{action}",
|
||||||
|
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
using System;
|
|
||||||
using Microsoft.Framework.OptionsModel;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Core.Filters
|
|
||||||
{
|
|
||||||
public class FormatFilter : IFormatFilter
|
|
||||||
{
|
|
||||||
public void OnResourceExecuting([NotNull] ResourceExecutingContext context)
|
|
||||||
{
|
|
||||||
var options = (IOptions<MvcOptions>)context.HttpContext.RequestServices.GetService(
|
|
||||||
typeof(IOptions<MvcOptions>));
|
|
||||||
string format = null;
|
|
||||||
|
|
||||||
if (context.RouteData.Values.ContainsKey("format"))
|
|
||||||
{
|
|
||||||
format = context.RouteData.Values["format"].ToString();
|
|
||||||
}
|
|
||||||
else if(context.HttpContext.Request.Query.ContainsKey("format"))
|
|
||||||
{
|
|
||||||
format = context.HttpContext.Request.Query.Get("format").ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format != null)
|
|
||||||
{
|
|
||||||
var contentType = options.Options.OutputFormatterOptions.GetContentTypeForFormat(format);
|
|
||||||
if (contentType == null)
|
|
||||||
{
|
|
||||||
// no contentType exists for the foramt, return 404
|
|
||||||
context.Result = new HttpNotFoundResult();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (context.Filters.Any(f => f is ProducesAttribute))
|
|
||||||
{
|
|
||||||
var produces = context.Filters.First(f => f is ProducesAttribute) as ProducesAttribute;
|
|
||||||
if(!produces.ContentTypes.Contains(contentType))
|
|
||||||
{
|
|
||||||
context.Result = new HttpNotFoundResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnResourceExecuted([NotNull] ResourceExecutedContext context)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnResultExecuting([NotNull]ResultExecutingContext context)
|
|
||||||
{
|
|
||||||
var options = (IOptions<MvcOptions>)context.HttpContext.RequestServices.GetService(
|
|
||||||
typeof(IOptions<MvcOptions>));
|
|
||||||
string format = null;
|
|
||||||
|
|
||||||
if (context.RouteData.Values.ContainsKey("format"))
|
|
||||||
{
|
|
||||||
format = context.RouteData.Values["format"].ToString();
|
|
||||||
}
|
|
||||||
else if (context.HttpContext.Request.Query.ContainsKey("format"))
|
|
||||||
{
|
|
||||||
format = context.HttpContext.Request.Query.Get("format").ToString();
|
|
||||||
}
|
|
||||||
if (format != null)
|
|
||||||
{
|
|
||||||
var contentType = options.Options.OutputFormatterOptions.GetContentTypeForFormat(format);
|
|
||||||
if (contentType != null)
|
|
||||||
{
|
|
||||||
var objectResult = context.Result as ObjectResult;
|
|
||||||
if (objectResult != null)
|
|
||||||
{
|
|
||||||
objectResult.ContentTypes.Clear();
|
|
||||||
objectResult.ContentTypes.Add(contentType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Result = new HttpStatusCodeResult(404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnResultExecuted([NotNull]ResultExecutedContext context)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.Description;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This will look at the format parameter if present in the route data or query data and
|
||||||
|
/// sets the content type in ObjectResult corresponding to the format value.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class FormatFilterAttribute : Attribute, IFormatFilter, IResourceFilter, IResultFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// As a resourceFilter, this filter looks at the request and rejects it
|
||||||
|
/// before going ahead if
|
||||||
|
/// 1. The Format in the request doesnt match any format in the map.
|
||||||
|
/// 2. If there is a conflicting producesFilter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public void OnResourceExecuting([NotNull] ResourceExecutingContext context)
|
||||||
|
{
|
||||||
|
var format = GetFormat(context);
|
||||||
|
|
||||||
|
if (format != null && !string.IsNullOrEmpty(format.ToString()))
|
||||||
|
{
|
||||||
|
var formatContentType = GetContentType(format, context);
|
||||||
|
if (formatContentType == null)
|
||||||
|
{
|
||||||
|
// no contentType exists for the format, return 404
|
||||||
|
context.Result = new HttpNotFoundResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var responseTypeFilters = context.Filters.OfType<IApiResponseMetadataProvider>();
|
||||||
|
if (responseTypeFilters.Count() != 0)
|
||||||
|
{
|
||||||
|
var contentTypes = new List<MediaTypeHeaderValue>();
|
||||||
|
foreach (var filter in responseTypeFilters)
|
||||||
|
{
|
||||||
|
filter.SetContentTypes(contentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contentTypes.Any(c => c.IsSubsetOf(formatContentType)))
|
||||||
|
{
|
||||||
|
context.Result = new HttpNotFoundResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResourceExecuted([NotNull] ResourceExecutedContext context)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResultExecuting([NotNull] ResultExecutingContext context)
|
||||||
|
{
|
||||||
|
var format = GetFormat(context);
|
||||||
|
if (format != null)
|
||||||
|
{
|
||||||
|
var contentType = GetContentType(format, context);
|
||||||
|
Debug.Assert(contentType != null);
|
||||||
|
|
||||||
|
var objectResult = context.Result as ObjectResult;
|
||||||
|
if (objectResult != null)
|
||||||
|
{
|
||||||
|
objectResult.ContentTypes.Clear();
|
||||||
|
objectResult.ContentTypes.Add(contentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResultExecuted([NotNull] ResultExecutedContext context)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaTypeHeaderValue GetContentTypeForCurrentRequest(FilterContext context)
|
||||||
|
{
|
||||||
|
var format = GetFormat(context);
|
||||||
|
if (format != null && !string.IsNullOrEmpty(format.ToString()))
|
||||||
|
{
|
||||||
|
return GetContentType(format, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetFormat(FilterContext context)
|
||||||
|
{
|
||||||
|
object format = null;
|
||||||
|
|
||||||
|
if (!context.RouteData.Values.TryGetValue("format", out format))
|
||||||
|
{
|
||||||
|
if (context.HttpContext.Request.Query.ContainsKey("format"))
|
||||||
|
{
|
||||||
|
format = context.HttpContext.Request.Query.Get("format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaTypeHeaderValue GetContentType(object format, FilterContext context)
|
||||||
|
{
|
||||||
|
Debug.Assert(format != null);
|
||||||
|
var options = context.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
|
||||||
|
var contentType = options.Options.FormatterMappings.GetContentTypeForFormat(format.ToString());
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
using System;
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Core.Filters
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public interface IFormatFilter : IResourceFilter, IResultFilter
|
/// <summary>
|
||||||
|
/// Implement this interface if you want to have your own implementation of FormatFilter
|
||||||
|
/// </summary>
|
||||||
|
public interface IFormatFilter : IFilter
|
||||||
{
|
{
|
||||||
|
MediaTypeHeaderValue GetContentTypeForCurrentRequest(FilterContext context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,6 @@ using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.Description;
|
using Microsoft.AspNet.Mvc.Description;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -37,7 +36,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
// Check if FormatFilter has already set the content type
|
// Check if FormatFilter has already set the content type
|
||||||
// If it has, dont override it
|
// If it has, dont override it
|
||||||
if (objectResult.ContentTypes.Count == 0)
|
var formatFilter = context.Filters.OfType<IFormatFilter>().LastOrDefault();
|
||||||
|
if (formatFilter == null || formatFilter.GetContentTypeForCurrentRequest(context) == null)
|
||||||
{
|
{
|
||||||
SetContentTypes(objectResult.ContentTypes);
|
SetContentTypes(objectResult.ContentTypes);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Globalization;
|
||||||
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// These options are used to specify mapping between the Url Format and corresponding ContentType.
|
||||||
|
/// </summary>
|
||||||
|
public class FormatterMappings
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, MediaTypeHeaderValue> _map =
|
||||||
|
new Dictionary<string, MediaTypeHeaderValue>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public void SetFormatMapping([NotNull] string format, [NotNull] MediaTypeHeaderValue contentType)
|
||||||
|
{
|
||||||
|
if (contentType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException((Resources.ArgumentCannotBeNullOrEmpty), "contentType");
|
||||||
|
}
|
||||||
|
|
||||||
|
format = RemovePeriodIfPresent(format);
|
||||||
|
_map[format] = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaTypeHeaderValue GetContentTypeForFormat(string format)
|
||||||
|
{
|
||||||
|
format = RemovePeriodIfPresent(format);
|
||||||
|
MediaTypeHeaderValue value = null;
|
||||||
|
_map.TryGetValue(format, out value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string RemovePeriodIfPresent(string format)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(format))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "format");
|
||||||
|
}
|
||||||
|
if (format.StartsWith("."))
|
||||||
|
{
|
||||||
|
format = format.Substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
private AntiForgeryOptions _antiForgeryOptions = new AntiForgeryOptions();
|
private AntiForgeryOptions _antiForgeryOptions = new AntiForgeryOptions();
|
||||||
private int _maxModelStateErrors = ModelStateDictionary.DefaultMaxAllowedErrors;
|
private int _maxModelStateErrors = ModelStateDictionary.DefaultMaxAllowedErrors;
|
||||||
//private OutputFormatterOptions _outputFormatterOptions = new OutputFormatterOptions();
|
|
||||||
|
|
||||||
public MvcOptions()
|
public MvcOptions()
|
||||||
{
|
{
|
||||||
|
|
@ -29,7 +28,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
OutputFormatters = new List<OutputFormatterDescriptor>();
|
OutputFormatters = new List<OutputFormatterDescriptor>();
|
||||||
InputFormatters = new List<InputFormatterDescriptor>();
|
InputFormatters = new List<InputFormatterDescriptor>();
|
||||||
Filters = new List<IFilter>();
|
Filters = new List<IFilter>();
|
||||||
OutputFormatterOptions = new OutputFormatterOptions();
|
FormatterMappings = new FormatterMappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -55,7 +54,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputFormatterOptions OutputFormatterOptions { get; }
|
public FormatterMappings FormatterMappings { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of <see cref="IFilter"/> which are used to construct filters that
|
/// Gets a list of <see cref="IFilter"/> which are used to construct filters that
|
||||||
|
|
@ -69,16 +68,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<OutputFormatterDescriptor> OutputFormatters { get; private set; }
|
public List<OutputFormatterDescriptor> OutputFormatters { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the mapping for output format specified in URL (extension) and content type
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="format">URL extension for output format</param>
|
|
||||||
/// <param name="contentType">Content type mapping to the format</param>
|
|
||||||
public void AddFormatMapping(string format, MediaTypeHeaderValue contentType)
|
|
||||||
{
|
|
||||||
OutputFormatterOptions.AddFormatMapping(format, contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of the <see cref="InputFormatterDescriptor" /> which are used to construct
|
/// Gets a list of the <see cref="InputFormatterDescriptor" /> which are used to construct
|
||||||
/// a list of <see cref="IInputFormatter"/> by <see cref="IInputFormattersProvider"/>.
|
/// a list of <see cref="IInputFormatter"/> by <see cref="IInputFormattersProvider"/>.
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Core
|
|
||||||
{
|
|
||||||
public class OutputFormatterOptions
|
|
||||||
{
|
|
||||||
private Dictionary<string, MediaTypeHeaderValue> map = new Dictionary<string, MediaTypeHeaderValue>();
|
|
||||||
|
|
||||||
public void AddFormatMapping(string format, MediaTypeHeaderValue contentType)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(format) && contentType != null)
|
|
||||||
{
|
|
||||||
if(format.StartsWith("."))
|
|
||||||
{
|
|
||||||
format = format.TrimStart('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
map[format.ToLower()] = contentType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MediaTypeHeaderValue GetContentTypeForFormat(string format)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(format))
|
|
||||||
{
|
|
||||||
if (format.StartsWith("."))
|
|
||||||
{
|
|
||||||
format = format.TrimStart('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.ContainsKey(format.ToLower()))
|
|
||||||
{
|
|
||||||
return map[format.ToLower()];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,9 +13,7 @@
|
||||||
"Microsoft.AspNet.Routing": "1.0.0-*",
|
"Microsoft.AspNet.Routing": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Security": "1.0.0-*",
|
"Microsoft.AspNet.Security": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Security.DataProtection": "1.0.0-*",
|
"Microsoft.AspNet.Security.DataProtection": "1.0.0-*",
|
||||||
"Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" },
|
"Microsoft.Framework.Runtime.Interfaces": { "version": "1.0.0-*", "type": "build" }
|
||||||
"Microsoft.Net.Http": "2.2.13.0",
|
|
||||||
"Microsoft.Net.Http.Server": "1.0.0.0-rc1-11332"
|
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"aspnet50": {},
|
"aspnet50": {},
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
options.OutputFormatters.Add(new StringOutputFormatter());
|
options.OutputFormatters.Add(new StringOutputFormatter());
|
||||||
options.OutputFormatters.Add(new JsonOutputFormatter());
|
options.OutputFormatters.Add(new JsonOutputFormatter());
|
||||||
|
|
||||||
// Set up default mapping for xml and json extensions to content type
|
// Set up default mapping for json extensions to content type
|
||||||
options.AddFormatMapping("json", MediaTypeHeaderValue.Parse("application/json"));
|
options.FormatterMappings.SetFormatMapping("json", MediaTypeHeaderValue.Parse("application/json"));
|
||||||
options.AddFormatMapping("xml", MediaTypeHeaderValue.Parse("application/xml"));
|
|
||||||
|
|
||||||
// Set up default input formatters.
|
// Set up default input formatters.
|
||||||
options.InputFormatters.Add(new JsonInputFormatter());
|
options.InputFormatters.Add(new JsonInputFormatter());
|
||||||
|
|
|
||||||
|
|
@ -1,223 +1,348 @@
|
||||||
using System;
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
using System.Threading.Tasks;
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
using Microsoft.AspNet.Mvc.Core.Filters;
|
|
||||||
using Microsoft.AspNet.PipelineCore;
|
using System;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Http.Core;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
#if ASPNET50
|
#if ASPNET50
|
||||||
using Moq;
|
using Moq;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using System.Net;
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public enum FormatPlace
|
|
||||||
{
|
|
||||||
RouteData,
|
|
||||||
QueryData,
|
|
||||||
RouteAndQueryData
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FormatFilterTests
|
public class FormatFilterTests
|
||||||
{
|
{
|
||||||
|
public enum FormatSource
|
||||||
|
{
|
||||||
|
RouteData,
|
||||||
|
QueryData,
|
||||||
|
RouteAndQueryData
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ASPNET50
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("json", FormatPlace.RouteData, "application/json")]
|
[InlineData("json", FormatSource.RouteData, "application/json")]
|
||||||
[InlineData("json", FormatPlace.QueryData, "application/json")]
|
[InlineData("json", FormatSource.QueryData, "application/json")]
|
||||||
[InlineData("json", FormatPlace.RouteAndQueryData, "application/json")]
|
[InlineData("json", FormatSource.RouteAndQueryData, "application/json")]
|
||||||
public void FormatFilter_ContextContainsFormat_DefaultFormat(string format,
|
public void FormatFilter_ContextContainsFormat_DefaultFormat(
|
||||||
FormatPlace place,
|
string format,
|
||||||
|
FormatSource place,
|
||||||
string contentType)
|
string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
var mediaType = MediaTypeHeaderValue.Parse("application/json");
|
||||||
var context = CreateResultExecutingContext(format, place);
|
var resultExecutingContext = CreateResultExecutingContext(
|
||||||
var filter = new FormatFilter();
|
format,
|
||||||
|
FormatSource.RouteData);
|
||||||
|
var resourceExecutingContext = CreateResourceExecutingContext(
|
||||||
|
new IFilter[] { },
|
||||||
|
format,
|
||||||
|
FormatSource.RouteData);
|
||||||
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
filter.OnResultExecuting(context);
|
filter.OnResourceExecuting(resourceExecutingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(resourceExecutingContext.Result);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
filter.OnResultExecuting(resultExecutingContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var objectResult = context.Result as ObjectResult;
|
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
|
||||||
Assert.Equal(1, objectResult.ContentTypes.Count);
|
Assert.Equal(1, objectResult.ContentTypes.Count);
|
||||||
ValidateMediaType(mediaType, objectResult.ContentTypes[0]);
|
AssertMediaTypesEqual(mediaType, objectResult.ContentTypes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FormatFilter_ContextContainsFormat_InRouteAndQueryData()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mediaType = MediaTypeHeaderValue.Parse("application/json");
|
||||||
|
|
||||||
|
var httpContext = CreateMockHttpContext();
|
||||||
|
|
||||||
|
// Routedata contains json
|
||||||
|
var data = new RouteData();
|
||||||
|
data.Values.Add("format", "json");
|
||||||
|
|
||||||
|
// Query contains xml
|
||||||
|
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true);
|
||||||
|
httpContext.Setup(c => c.Request.Query.Get("format")).Returns("xml");
|
||||||
|
var ac = new ActionContext(httpContext.Object, data, new ActionDescriptor());
|
||||||
|
|
||||||
|
var resultExecutingContext = new ResultExecutingContext(
|
||||||
|
ac,
|
||||||
|
new IFilter[] { },
|
||||||
|
new ObjectResult("Hello!"),
|
||||||
|
controller: new object());
|
||||||
|
|
||||||
|
var resourceExecutingContext = new ResourceExecutingContext(
|
||||||
|
ac,
|
||||||
|
new IFilter[] { });
|
||||||
|
|
||||||
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
filter.OnResourceExecuting(resourceExecutingContext);
|
||||||
|
filter.OnResultExecuting(resultExecutingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
|
||||||
|
Assert.Equal(1, objectResult.ContentTypes.Count);
|
||||||
|
AssertMediaTypesEqual(mediaType, objectResult.ContentTypes[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("foo", FormatPlace.RouteData, "application/foo")]
|
[InlineData("foo", FormatSource.RouteData, "application/foo")]
|
||||||
[InlineData("foo", FormatPlace.QueryData, "application/foo")]
|
[InlineData("foo", FormatSource.QueryData, "application/foo")]
|
||||||
[InlineData("foo", FormatPlace.RouteAndQueryData, "application/foo")]
|
[InlineData("foo", FormatSource.RouteAndQueryData, "application/foo")]
|
||||||
public void FormatFilter_ContextContainsFormat_Custom(
|
public void FormatFilter_ContextContainsFormat_Custom(
|
||||||
string format,
|
string format,
|
||||||
FormatPlace place,
|
FormatSource place,
|
||||||
string contentType)
|
string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
||||||
var context = CreateResultExecutingContext(format, place);
|
var resultExecutingContext = CreateResultExecutingContext(format, place);
|
||||||
var options = (IOptions<MvcOptions>)context.HttpContext.RequestServices.GetService(
|
var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { }, format, place);
|
||||||
typeof(IOptions<MvcOptions>));
|
var options = resultExecutingContext.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
|
||||||
options.Options.AddFormatMapping(format, MediaTypeHeaderValue.Parse(contentType));
|
options.Options.FormatterMappings.SetFormatMapping(format, MediaTypeHeaderValue.Parse(contentType));
|
||||||
var filter = new FormatFilter();
|
|
||||||
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
filter.OnResultExecuting(context);
|
filter.OnResourceExecuting(resourceExecutingContext);
|
||||||
|
filter.OnResultExecuting(resultExecutingContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var objectResult = context.Result as ObjectResult;
|
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
|
||||||
Assert.Equal(1, objectResult.ContentTypes.Count);
|
Assert.Equal(1, objectResult.ContentTypes.Count);
|
||||||
ValidateMediaType(mediaType, objectResult.ContentTypes[0]);
|
AssertMediaTypesEqual(mediaType, objectResult.ContentTypes[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("foo", FormatPlace.RouteData, "application/foo")]
|
[InlineData("foo", FormatSource.RouteData, "application/foo")]
|
||||||
public void FormatFilter_ContextContainsFormat_NonExisting(
|
[InlineData("foo", FormatSource.QueryData, "application/foo")]
|
||||||
|
public void FormatFilter_ContextContainsNonExistingFormat(
|
||||||
string format,
|
string format,
|
||||||
FormatPlace place,
|
FormatSource place,
|
||||||
string contentType)
|
string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
||||||
var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { }, format, place);
|
var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { }, format, place);
|
||||||
var filter = new FormatFilter();
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
filter.OnResourceExecuting(resourceExecutingContext);
|
filter.OnResourceExecuting(resourceExecutingContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var actionResult = resourceExecutingContext.Result;
|
var actionResult = resourceExecutingContext.Result;
|
||||||
Assert.True(actionResult is HttpNotFoundResult);
|
Assert.IsType<HttpNotFoundResult>(actionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FormatFilter_ContextDoesntContainFormat()
|
public void FormatFilter_ContextDoesntContainFormat()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { });
|
var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { });
|
||||||
var filter = new FormatFilter();
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
filter.OnResourceExecuting(resourceExecutingContext);
|
filter.OnResourceExecuting(resourceExecutingContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var result = resourceExecutingContext.Result as IActionResult;
|
Assert.Null(resourceExecutingContext.Result);
|
||||||
Assert.False(result is HttpNotFoundResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("json", FormatPlace.RouteData, "application/json")]
|
[InlineData("json", FormatSource.RouteData, "application/json")]
|
||||||
[InlineData("json", FormatPlace.QueryData, "application/json")]
|
[InlineData("json", FormatSource.QueryData, "application/json")]
|
||||||
public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_Matching(
|
public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_Matching(
|
||||||
string format,
|
string format,
|
||||||
FormatPlace place,
|
FormatSource place,
|
||||||
string contentType)
|
string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var produces = new ProducesAttribute(contentType, new string[] { "application/foo", "text/bar" });
|
var produces = new ProducesAttribute(contentType, new string[] { "application/foo", "text/bar" });
|
||||||
var context = CreateResourceExecutingContext(new IFilter[] { produces }, format, place);
|
var context = CreateResourceExecutingContext(new IFilter[] { produces }, format, place);
|
||||||
var filter = new FormatFilter();
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
filter.OnResourceExecuting(context);
|
filter.OnResourceExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var result = context.Result as IActionResult;
|
Assert.Null(context.Result);
|
||||||
Assert.False(result is HttpNotFoundResult);
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_WildCardMatching()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var produces = new ProducesAttribute(
|
||||||
|
"application/baz",
|
||||||
|
new string[] { "application/foo", "text/bar" });
|
||||||
|
var context = CreateResourceExecutingContext(new IFilter[] { produces }, "star", FormatSource.RouteData);
|
||||||
|
var options = context.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
|
||||||
|
options.Options.FormatterMappings.SetFormatMapping("star", MediaTypeHeaderValue.Parse("application/*"));
|
||||||
|
|
||||||
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
filter.OnResourceExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(context.Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("json", FormatPlace.RouteData, "application/json")]
|
[InlineData("json", FormatSource.RouteData, "application/json")]
|
||||||
[InlineData("json", FormatPlace.QueryData, "application/json")]
|
[InlineData("json", FormatSource.QueryData, "application/json")]
|
||||||
public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_Conflicting(
|
public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_Conflicting(
|
||||||
string format,
|
string format,
|
||||||
FormatPlace place,
|
FormatSource place,
|
||||||
string contentType)
|
string contentType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
||||||
var produces = new ProducesAttribute("application/xml", new string[] { "application/foo", "text/bar" });
|
var produces = new ProducesAttribute("application/xml", new string[] { "application/foo", "text/bar" });
|
||||||
var context = CreateResourceExecutingContext(new IFilter[] { produces }, format, place);
|
var context = CreateResourceExecutingContext(new IFilter[] { produces }, format, place);
|
||||||
var filter = new FormatFilter();
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
filter.OnResourceExecuting(context);
|
filter.OnResourceExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var result = context.Result as IActionResult;
|
var result = Assert.IsType<HttpNotFoundResult>(context.Result);
|
||||||
Assert.True(result is HttpNotFoundResult);
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("", FormatSource.RouteData)]
|
||||||
|
[InlineData(null, FormatSource.QueryData)]
|
||||||
|
[InlineData("", FormatSource.RouteData)]
|
||||||
|
[InlineData(null, FormatSource.QueryData)]
|
||||||
|
public void FormatFilter_ContextContainsFormat_Invalid(
|
||||||
|
string format,
|
||||||
|
FormatSource place)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceExecutingContext = CreateResourceExecutingContext(
|
||||||
|
new IFilter[] { },
|
||||||
|
format,
|
||||||
|
FormatSource.RouteData);
|
||||||
|
var filter = new FormatFilterAttribute();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
filter.OnResourceExecuting(resourceExecutingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(resourceExecutingContext.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("json", FormatSource.RouteData, "application/json")]
|
||||||
|
[InlineData("json", FormatSource.QueryData, "application/json")]
|
||||||
|
[InlineData("", FormatSource.RouteAndQueryData, null)]
|
||||||
|
[InlineData(null, FormatSource.RouteAndQueryData, null)]
|
||||||
|
public void FormatFilter_GetContentTypeForRequest(
|
||||||
|
string format,
|
||||||
|
FormatSource place,
|
||||||
|
string contentType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resourceExecutingContext = CreateResourceExecutingContext(
|
||||||
|
new IFilter[] { },
|
||||||
|
format,
|
||||||
|
FormatSource.RouteData);
|
||||||
|
var filter = new FormatFilterAttribute();
|
||||||
|
var returnContentType = filter.GetContentTypeForCurrentRequest(resourceExecutingContext);
|
||||||
|
|
||||||
|
|
||||||
|
MediaTypeHeaderValue mediaType = null;
|
||||||
|
if (returnContentType != null)
|
||||||
|
{
|
||||||
|
mediaType = MediaTypeHeaderValue.Parse("application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(mediaType, returnContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResourceExecutingContext CreateResourceExecutingContext(
|
private static ResourceExecutingContext CreateResourceExecutingContext(
|
||||||
IFilter[] filters,
|
IFilter[] filters,
|
||||||
string format = null,
|
string format = null,
|
||||||
FormatPlace? place = null)
|
FormatSource? place = null)
|
||||||
{
|
{
|
||||||
if(format == null || place == null)
|
if (format == null || place == null)
|
||||||
{
|
{
|
||||||
var context = new ResourceExecutingContext(
|
var context = new ResourceExecutingContext(
|
||||||
CreateActionContext(),
|
CreateActionContext(),
|
||||||
filters);
|
filters);
|
||||||
context.Result = new HttpStatusCodeResult(200);
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
var context1 = new ResourceExecutingContext(
|
var context1 = new ResourceExecutingContext(
|
||||||
CreateActionContext(format, place),
|
CreateActionContext(format, place),
|
||||||
filters);
|
filters);
|
||||||
context1.Result = new HttpStatusCodeResult(200);
|
|
||||||
return context1;
|
return context1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResultExecutingContext CreateResultExecutingContext(
|
private static ResultExecutingContext CreateResultExecutingContext(
|
||||||
string format = null,
|
string format = null,
|
||||||
FormatPlace? place = null)
|
FormatSource? place = null)
|
||||||
{
|
{
|
||||||
if (format == null || place == null)
|
if (format == null && place == null)
|
||||||
{
|
{
|
||||||
return new ResultExecutingContext(
|
return new ResultExecutingContext(
|
||||||
new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()),
|
new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()),
|
||||||
new IFilter[] { },
|
new IFilter[] { },
|
||||||
new ObjectResult("Some Value"));
|
new ObjectResult("Some Value"),
|
||||||
|
controller: new object());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResultExecutingContext(
|
return new ResultExecutingContext(
|
||||||
CreateActionContext(format, place),
|
CreateActionContext(format, place),
|
||||||
new IFilter[] { },
|
new IFilter[] { },
|
||||||
new ObjectResult("Some Value"));
|
new ObjectResult("Some Value"),
|
||||||
|
controller: new object());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActionContext CreateActionContext(string format = null, FormatPlace? place = null)
|
private static ActionContext CreateActionContext(string format = null, FormatSource? place = null)
|
||||||
{
|
{
|
||||||
var httpContext = CreateMockHttpContext();
|
var httpContext = CreateMockHttpContext();
|
||||||
|
var data = new RouteData();
|
||||||
|
|
||||||
if (place == FormatPlace.RouteData || place == FormatPlace.RouteAndQueryData)
|
if (place == FormatSource.RouteData || place == FormatSource.RouteAndQueryData)
|
||||||
{
|
{
|
||||||
var data = new RouteData();
|
|
||||||
data.Values.Add("format", format);
|
data.Values.Add("format", format);
|
||||||
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false);
|
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false);
|
||||||
return new ActionContext(httpContext.Object, data, new ActionDescriptor());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (place == FormatPlace.QueryData || place == FormatPlace.RouteAndQueryData)
|
if (place == FormatSource.QueryData || place == FormatSource.RouteAndQueryData)
|
||||||
{
|
{
|
||||||
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true);
|
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true);
|
||||||
httpContext.Setup(c => c.Request.Query.Get("format")).Returns(format);
|
httpContext.Setup(c => c.Request.Query.Get("format")).Returns(format);
|
||||||
return new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
|
|
||||||
}
|
}
|
||||||
else if(place == null && format == null)
|
else if (place == null && format == null)
|
||||||
{
|
{
|
||||||
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false);
|
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false);
|
||||||
return new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return new ActionContext(httpContext.Object, data, new ActionDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Mock<HttpContext> CreateMockHttpContext()
|
private static Mock<HttpContext> CreateMockHttpContext()
|
||||||
{
|
{
|
||||||
MvcOptions options = new MvcOptions();
|
var options = new MvcOptions();
|
||||||
MvcOptionsSetup.ConfigureMvc(options);
|
MvcOptionsSetup.ConfigureMvc(options);
|
||||||
var mvcOptions = new Mock<IOptions<MvcOptions>>();
|
var mvcOptions = new Mock<IOptions<MvcOptions>>();
|
||||||
mvcOptions.Setup(o => o.Options).Returns(options);
|
mvcOptions.Setup(o => o.Options).Returns(options);
|
||||||
|
|
@ -236,7 +361,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
return httpContext;
|
return httpContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ValidateMediaType(MediaTypeHeaderValue expectedMediaType, MediaTypeHeaderValue actualMediaType)
|
private static void AssertMediaTypesEqual(
|
||||||
|
MediaTypeHeaderValue expectedMediaType,
|
||||||
|
MediaTypeHeaderValue actualMediaType)
|
||||||
{
|
{
|
||||||
Assert.Equal(expectedMediaType.MediaType, actualMediaType.MediaType);
|
Assert.Equal(expectedMediaType.MediaType, actualMediaType.MediaType);
|
||||||
Assert.Equal(expectedMediaType.SubType, actualMediaType.SubType);
|
Assert.Equal(expectedMediaType.SubType, actualMediaType.SubType);
|
||||||
|
|
@ -249,5 +376,6 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
Assert.Equal(item.Value, NameValueHeaderValue.Find(actualMediaType.Parameters, item.Name).Value);
|
Assert.Equal(item.Value, NameValueHeaderValue.Find(actualMediaType.Parameters, item.Name).Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,6 +9,10 @@ using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
#if ASPNET50
|
||||||
|
using Moq;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Test
|
namespace Microsoft.AspNet.Mvc.Test
|
||||||
{
|
{
|
||||||
public class ProducesAttributeTests
|
public class ProducesAttributeTests
|
||||||
|
|
@ -20,7 +24,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var mediaType1 = MediaTypeHeaderValue.Parse("application/json");
|
var mediaType1 = MediaTypeHeaderValue.Parse("application/json");
|
||||||
var mediaType2 = MediaTypeHeaderValue.Parse("text/json;charset=utf-8");
|
var mediaType2 = MediaTypeHeaderValue.Parse("text/json;charset=utf-8");
|
||||||
var producesContentAttribute = new ProducesAttribute("application/json", "text/json;charset=utf-8");
|
var producesContentAttribute = new ProducesAttribute("application/json", "text/json;charset=utf-8");
|
||||||
var resultExecutingContext = CreateResultExecutingContext(producesContentAttribute);
|
var resultExecutingContext = CreateResultExecutingContext(new IFilter[] { producesContentAttribute });
|
||||||
var next = new ResultExecutionDelegate(
|
var next = new ResultExecutionDelegate(
|
||||||
() => Task.FromResult(CreateResultExecutedContext(resultExecutingContext)));
|
() => Task.FromResult(CreateResultExecutedContext(resultExecutingContext)));
|
||||||
|
|
||||||
|
|
@ -33,6 +37,33 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
ValidateMediaType(mediaType1, objectResult.ContentTypes[0]);
|
ValidateMediaType(mediaType1, objectResult.ContentTypes[0]);
|
||||||
ValidateMediaType(mediaType2, objectResult.ContentTypes[1]);
|
ValidateMediaType(mediaType2, objectResult.ContentTypes[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProducesContentAttribute_FormatFilterAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mediaType1 = MediaTypeHeaderValue.Parse("application/xml");
|
||||||
|
var mediaType2 = MediaTypeHeaderValue.Parse("application/json");
|
||||||
|
var producesContentAttribute = new ProducesAttribute("application/xml");
|
||||||
|
|
||||||
|
var formatFilter = new Mock<IFormatFilter>();
|
||||||
|
formatFilter.Setup(f => f.GetContentTypeForCurrentRequest(It.IsAny<FilterContext>()))
|
||||||
|
.Returns(mediaType2);
|
||||||
|
|
||||||
|
var filters = new IFilter[] { producesContentAttribute, formatFilter.Object };
|
||||||
|
var resultExecutingContext = CreateResultExecutingContext(filters);
|
||||||
|
|
||||||
|
var next = new ResultExecutionDelegate(
|
||||||
|
() => Task.FromResult(CreateResultExecutedContext(resultExecutingContext)));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await producesContentAttribute.OnResultExecutionAsync(resultExecutingContext, next);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
|
||||||
|
Assert.Equal(0, objectResult.ContentTypes.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("", "")]
|
[InlineData("", "")]
|
||||||
|
|
@ -95,13 +126,12 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
return new ResultExecutedContext(context, context.Filters, context.Result, context.Controller);
|
return new ResultExecutedContext(context, context.Filters, context.Result, context.Controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResultExecutingContext CreateResultExecutingContext(IFilter filter)
|
private static ResultExecutingContext CreateResultExecutingContext(IFilter[] filters)
|
||||||
{
|
{
|
||||||
return new ResultExecutingContext(
|
return new ResultExecutingContext(
|
||||||
CreateActionContext(),
|
CreateActionContext(),
|
||||||
new IFilter[] { filter, },
|
filters,
|
||||||
new ObjectResult("Some Value"),
|
new ObjectResult("Some Value"));
|
||||||
controller: new object());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActionContext CreateActionContext()
|
private static ActionContext CreateActionContext()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
public class FormatterMappingsTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(".xml", "application/xml", "xml")]
|
||||||
|
[InlineData("json", "application/json", "JSON")]
|
||||||
|
[InlineData(".foo", "text/foo", "Foo")]
|
||||||
|
[InlineData(".Json", "application/json", "json")]
|
||||||
|
[InlineData("FOo", "text/foo", "FOO")]
|
||||||
|
public void FormatterMappings_SetFormatMapping_DiffSetGetFormat(string setFormat, string contentType, string getFormat)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
||||||
|
var options = new FormatterMappings();
|
||||||
|
options.SetFormatMapping(setFormat, mediaType);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var returnmediaType = options.GetContentTypeForFormat(getFormat);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(mediaType, returnmediaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("xml", null)]
|
||||||
|
[InlineData(".json", null)]
|
||||||
|
[InlineData(null, "application/json")]
|
||||||
|
[InlineData("", "text/foo")]
|
||||||
|
public void FormatterMappings_SetFormatMapping_Invalid(string format, string contentType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
MediaTypeHeaderValue mediaType = null;
|
||||||
|
if (!string.IsNullOrEmpty(contentType))
|
||||||
|
{
|
||||||
|
mediaType = MediaTypeHeaderValue.Parse(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new FormatterMappings();
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
Assert.Throws<ArgumentException>(() => options.SetFormatMapping(format, mediaType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
using System;
|
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
|
||||||
{
|
|
||||||
public class OutputFormatterOptionsTest
|
|
||||||
{
|
|
||||||
[Theory]
|
|
||||||
[InlineData("xml", "application/xml")]
|
|
||||||
[InlineData("json", "application/json")]
|
|
||||||
[InlineData("foo", "text/foo")]
|
|
||||||
[InlineData(".json", "application/json")]
|
|
||||||
[InlineData(".foo", "text/foo")]
|
|
||||||
public void OutputFormatterOptions_AddFormatMapping_Valid(string format, string contentType)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
|
||||||
OutputFormatterOptions options = new OutputFormatterOptions();
|
|
||||||
options.AddFormatMapping(format, mediaType);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var returnmediaType = options.GetContentTypeForFormat(format);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(mediaType, returnmediaType);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(".xml", "application/xml", "xml")]
|
|
||||||
[InlineData("json", "application/json", "JSON")]
|
|
||||||
[InlineData(".foo", "text/foo", "Foo")]
|
|
||||||
[InlineData(".Json", "application/json", "json")]
|
|
||||||
[InlineData("FOo", "text/foo", "FOO")]
|
|
||||||
public void OutputFormatterOptions_AddFormatMapping_DiffSetGetFormat(string setFormat, string contentType, string getFormat)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
|
||||||
OutputFormatterOptions options = new OutputFormatterOptions();
|
|
||||||
options.AddFormatMapping(setFormat, mediaType);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var returnmediaType = options.GetContentTypeForFormat(getFormat);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(mediaType, returnmediaType);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("xml", null)]
|
|
||||||
[InlineData(".json", null)]
|
|
||||||
[InlineData(null, "application/json")]
|
|
||||||
[InlineData("", "text/foo")]
|
|
||||||
public void OutputFormatterOptions_AddFormatMapping_Invalid(string format, string contentType)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
MediaTypeHeaderValue mediaType = null;
|
|
||||||
if (!string.IsNullOrEmpty(contentType))
|
|
||||||
{
|
|
||||||
mediaType = MediaTypeHeaderValue.Parse(contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputFormatterOptions options = new OutputFormatterOptions();
|
|
||||||
options.AddFormatMapping(format, mediaType);
|
|
||||||
|
|
||||||
// Act and Assert
|
|
||||||
Assert.Throws<ArgumentException>(() => options.GetContentTypeForFormat(format));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -245,6 +245,119 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/foo.bar", "foo", "bar", null)]
|
||||||
|
[InlineData("moo/{p1?}", "/moo/foo", "foo", null, null)]
|
||||||
|
[InlineData("moo/{p1?}", "/moo", null, null, null)]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/foo", "foo", null, null)]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/foo..bar", "foo.", "bar", null)]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/foo.moo.bar", "foo.moo", "bar", null)]
|
||||||
|
[InlineData("moo/{p1}.{p2}", "/moo/foo.bar", "foo", "bar", null)]
|
||||||
|
[InlineData("moo/foo.{p1}.{p2?}", "/moo/foo.moo.bar", "moo", "bar", null)]
|
||||||
|
[InlineData("moo/foo.{p1}.{p2?}", "/moo/foo.moo", "moo", null, null)]
|
||||||
|
[InlineData("moo/.{p2?}", "/moo/.foo", null, "foo", null)]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/....", "..", ".", null)]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/.bar", ".bar", null, null)]
|
||||||
|
[InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo.moo.bar", "foo", "moo", "bar")]
|
||||||
|
[InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo.moo", "foo", "moo", null)]
|
||||||
|
[InlineData("moo/{p1}.{p2}.{p3}.{p4?}", "/moo/foo.moo.bar", "foo", "moo", "bar")]
|
||||||
|
[InlineData("{p1}.{p2?}/{p3}", "/foo.moo/bar", "foo", "moo", "bar")]
|
||||||
|
[InlineData("{p1}.{p2?}/{p3}", "/foo/bar", "foo", null, "bar")]
|
||||||
|
[InlineData("{p1}.{p2?}/{p3}", "/.foo/bar", ".foo", null, "bar")]
|
||||||
|
public async Task AttributeRoute_WithOptionalCompositeParameter_Valid(
|
||||||
|
string template,
|
||||||
|
string request,
|
||||||
|
string p1,
|
||||||
|
string p2,
|
||||||
|
string p3)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedRouteGroup = string.Format("{0}&&{1}", 0, template);
|
||||||
|
|
||||||
|
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||||
|
var numberOfCalls = 0;
|
||||||
|
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||||
|
|
||||||
|
var next = new Mock<IRouter>();
|
||||||
|
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||||
|
.Callback(callBack)
|
||||||
|
.Returns(Task.FromResult(true))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var firstRoute = CreateMatchingEntry(next.Object, template, order: 0);
|
||||||
|
|
||||||
|
// We setup the route entries in reverse order of precedence to ensure that when we
|
||||||
|
// try to route the request, the route with a higher precedence gets tried first.
|
||||||
|
var matchingRoutes = new[] { firstRoute };
|
||||||
|
var linkGenerationEntries = Enumerable.Empty<AttributeRouteLinkGenerationEntry>();
|
||||||
|
var route = new AttributeRoute(next.Object, matchingRoutes, linkGenerationEntries, NullLoggerFactory.Instance);
|
||||||
|
var context = CreateRouteContext(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(context.IsHandled);
|
||||||
|
if (p1 != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(p1, context.RouteData.Values["p1"]);
|
||||||
|
}
|
||||||
|
if (p2 != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(p2, context.RouteData.Values["p2"]);
|
||||||
|
}
|
||||||
|
if (p3 != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(p3, context.RouteData.Values["p3"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/foo.")]
|
||||||
|
[InlineData("moo/{p1}.{p2?}", "/moo/.")]
|
||||||
|
[InlineData("moo/{p1}.{p2}", "/foo.")]
|
||||||
|
[InlineData("moo/{p1}.{p2}", "/foo")]
|
||||||
|
[InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo.moo.")]
|
||||||
|
[InlineData("moo/foo.{p2}.{p3?}", "/moo/bar.foo.moo")]
|
||||||
|
[InlineData("moo/foo.{p2}.{p3?}", "/moo/kungfoo.moo.bar")]
|
||||||
|
[InlineData("moo/foo.{p2}.{p3?}", "/moo/kungfoo.moo")]
|
||||||
|
[InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo")]
|
||||||
|
[InlineData("{p1}.{p2?}/{p3}", "/foo./bar")]
|
||||||
|
[InlineData("moo/.{p2?}", "/moo/.")]
|
||||||
|
[InlineData("{p1}.{p2}/{p3}", "/.foo/bar")]
|
||||||
|
public async Task AttributeRoute_WithOptionalCompositeParameter_Invalid(
|
||||||
|
string template,
|
||||||
|
string request)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedRouteGroup = string.Format("{0}&&{1}", 0, template);
|
||||||
|
|
||||||
|
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||||
|
var numberOfCalls = 0;
|
||||||
|
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||||
|
|
||||||
|
var next = new Mock<IRouter>();
|
||||||
|
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||||
|
.Callback(callBack)
|
||||||
|
.Returns(Task.FromResult(true))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var firstRoute = CreateMatchingEntry(next.Object, template, order: 0);
|
||||||
|
|
||||||
|
// We setup the route entries in reverse order of precedence to ensure that when we
|
||||||
|
// try to route the request, the route with a higher precedence gets tried first.
|
||||||
|
var matchingRoutes = new[] { firstRoute };
|
||||||
|
var linkGenerationEntries = Enumerable.Empty<AttributeRouteLinkGenerationEntry>();
|
||||||
|
var route = new AttributeRoute(next.Object, matchingRoutes, linkGenerationEntries, NullLoggerFactory.Instance);
|
||||||
|
var context = CreateRouteContext(request);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await route.RouteAsync(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(context.IsHandled);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("template/5", "template/{parameter:int}")]
|
[InlineData("template/5", "template/{parameter:int}")]
|
||||||
[InlineData("template/5", "template/{parameter}")]
|
[InlineData("template/5", "template/{parameter}")]
|
||||||
|
|
@ -1196,6 +1309,96 @@ namespace Microsoft.AspNet.Mvc.Routing
|
||||||
Assert.Equal("Store", path);
|
Assert.Equal("Store", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> OptionalParamValues
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new object[][]
|
||||||
|
{
|
||||||
|
// defaults
|
||||||
|
// ambient values
|
||||||
|
// values
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/{val1}/{val2}.{val3?}",
|
||||||
|
new {val1 = "someval1", val2 = "someval2", val3 = "someval3a"},
|
||||||
|
new {val3 = "someval3v"},
|
||||||
|
"Test/someval1/someval2.someval3v",
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/{val1}/{val2}.{val3?}",
|
||||||
|
new {val3 = "someval3a"},
|
||||||
|
new {val1 = "someval1", val2 = "someval2", val3 = "someval3v" },
|
||||||
|
"Test/someval1/someval2.someval3v",
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/{val1}/{val2}.{val3?}",
|
||||||
|
null,
|
||||||
|
new {val1 = "someval1", val2 = "someval2" },
|
||||||
|
"Test/someval1/someval2",
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/{val1}.{val2}.{val3}.{val4?}",
|
||||||
|
new {val1 = "someval1", val2 = "someval2" },
|
||||||
|
new {val4 = "someval4", val3 = "someval3" },
|
||||||
|
"Test/someval1.someval2.someval3.someval4",
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/{val1}.{val2}.{val3}.{val4?}",
|
||||||
|
new {val1 = "someval1", val2 = "someval2" },
|
||||||
|
new {val3 = "someval3" },
|
||||||
|
"Test/someval1.someval2.someval3",
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/.{val2?}",
|
||||||
|
null,
|
||||||
|
new {val2 = "someval2" },
|
||||||
|
"Test/.someval2",
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/.{val2?}",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"Test/",
|
||||||
|
},
|
||||||
|
new object[]
|
||||||
|
{
|
||||||
|
"Test/{val1}.{val2}",
|
||||||
|
new {val1 = "someval1", val2 = "someval2" },
|
||||||
|
new {val3 = "someval3" },
|
||||||
|
"Test/someval1.someval2?val3=someval3",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData("OptionalParamValues")]
|
||||||
|
public void AttributeRoute_GenerateLink_Match_WithOptionalParameters(
|
||||||
|
string template,
|
||||||
|
object ambientValues,
|
||||||
|
object values,
|
||||||
|
string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var entry = CreateGenerationEntry(template, null);
|
||||||
|
var route = CreateAttributeRoute(entry);
|
||||||
|
|
||||||
|
var context = CreateVirtualPathContext(values, ambientValues);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var path = route.GetVirtualPath(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, path);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AttributeRoute_CreatesNewRouteData()
|
public async Task AttributeRoute_CreatesNewRouteData()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -405,5 +405,35 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(HttpStatusCode.NotAcceptable, response.StatusCode);
|
Assert.Equal(HttpStatusCode.NotAcceptable, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProducesAttribute_And_FormatFilterAttribute_Conflicting()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var expectedContentType = MediaTypeHeaderValue.Parse("application/json");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/MethodWithFormatFilter.json");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProducesAttribute_And_FormatFilterAttribute_Collaborating()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/MethodWithFormatFilter");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var type = response.Content.Headers.ContentType;
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class FormatFilterTest
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services = TestHelper.CreateServices("FormatFilterWebSite");
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new FormatFilterWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormatFilter_NoExtensionInRequest()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/GetProduct/5");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(@"{""SampleInt"":5}", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormatFilter_ExtensionInRequest_Default()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/GetProduct/5.json");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(@"{""SampleInt"":5}", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormatFilter_ExtensionInRequest_Custom()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/GetProduct/5.custom");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(@"SampleInt:5", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormatFilter_ExtensionInRequest_NonExistant()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/GetProduct/5.xml");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormatFilter_And_ProducesFilter_Match()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/ProducesMethod/5.json");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(@"{""SampleInt"":5}", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormatFilter_And_ProducesFilter_Conflict()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FormatFilter/ProducesMethod/5.xml");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FormatFilter_And_OverrideProducesFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/ProducesDerived/ReturnClassName.json");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -44,7 +44,8 @@
|
||||||
"MvcTagHelpersWebSite": "1.0.0",
|
"MvcTagHelpersWebSite": "1.0.0",
|
||||||
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
|
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
|
||||||
"xunit.runner.kre": "1.0.0-*",
|
"xunit.runner.kre": "1.0.0-*",
|
||||||
"Microsoft.AspNet.WebUtilities": "1.0.0-*"
|
"Microsoft.AspNet.WebUtilities": "1.0.0-*",
|
||||||
|
"FormatFilterWebSite": "1.0.0-*"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"test": "xunit.runner.kre"
|
"test": "xunit.runner.kre"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using ConnegWebSite;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace ConnegWebsite
|
||||||
|
{
|
||||||
|
[Produces("application/FormatFilterController")]
|
||||||
|
public class FormatFilterController : Controller
|
||||||
|
{
|
||||||
|
[FormatFilter]
|
||||||
|
public User MethodWithFormatFilter()
|
||||||
|
{
|
||||||
|
return new User() { Name = "Joe", Address = "1 abc way" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,6 @@ namespace ConnegWebSite
|
||||||
public virtual string ReturnClassNameContentTypeOnDerivedAction()
|
public virtual string ReturnClassNameContentTypeOnDerivedAction()
|
||||||
{
|
{
|
||||||
return "ProducesContentBaseController";
|
return "ProducesContentBaseController";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +39,6 @@ namespace ConnegWebSite
|
||||||
{
|
{
|
||||||
// should be written using the content defined at derived class's class.
|
// should be written using the content defined at derived class's class.
|
||||||
return "ProducesContentOnClassController";
|
return "ProducesContentOnClassController";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -31,7 +31,7 @@ namespace ConnegWebSite
|
||||||
app.UseMvc(routes =>
|
app.UseMvc(routes =>
|
||||||
{
|
{
|
||||||
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
||||||
defaults: new { controller = "Home", action = "Index" });
|
defaults: new { controller = "Home", action = "Index" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,9 @@
|
||||||
<DevelopmentServerPort>49641</DevelopmentServerPort>
|
<DevelopmentServerPort>49641</DevelopmentServerPort>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Security.Claims;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Security;
|
using Microsoft.AspNet.Security;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
namespace FiltersWebSite
|
namespace FiltersWebSite
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FormatFilterWebSite
|
||||||
|
{
|
||||||
|
[FormatFilter]
|
||||||
|
public class FormatFilterController : Controller
|
||||||
|
{
|
||||||
|
public Product GetProduct(int id)
|
||||||
|
{
|
||||||
|
return new Product() { SampleInt = id };
|
||||||
|
}
|
||||||
|
|
||||||
|
[Produces("application/custom", "application/json", "text/json")]
|
||||||
|
public Product ProducesMethod(int id)
|
||||||
|
{
|
||||||
|
return new Product() { SampleInt = id };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FormatFilterWebSite
|
||||||
|
{
|
||||||
|
public class ProducesBaseController : Controller
|
||||||
|
{
|
||||||
|
[Produces("application/custom_ProducesBaseController_Action")]
|
||||||
|
public virtual string ReturnClassName()
|
||||||
|
{
|
||||||
|
// Should be written using the action's content type. Overriding the one at the class.
|
||||||
|
return "ProducesBaseController";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FormatFilterWebSite
|
||||||
|
{
|
||||||
|
[Produces("application/custom_ProducesOverrideController")]
|
||||||
|
public class ProducesOverrideController : ProducesBaseController
|
||||||
|
{
|
||||||
|
[FormatFilter]
|
||||||
|
public override string ReturnClassName()
|
||||||
|
{
|
||||||
|
// should be written using the content defined at base class's action.
|
||||||
|
return "ProducesOverrideController";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace FormatFilterWebSite
|
||||||
|
{
|
||||||
|
public class CustomFormatter : OutputFormatter
|
||||||
|
{
|
||||||
|
public string ContentType { get; private set; }
|
||||||
|
|
||||||
|
public CustomFormatter(string contentType)
|
||||||
|
{
|
||||||
|
ContentType = contentType;
|
||||||
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(contentType));
|
||||||
|
SupportedEncodings.Add(Encoding.GetEncoding("utf-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
||||||
|
{
|
||||||
|
if (base.CanWriteResult(context, contentType))
|
||||||
|
{
|
||||||
|
var actionReturn = context.Object as Product;
|
||||||
|
if (actionReturn != null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task WriteResponseBodyAsync(OutputFormatterContext context)
|
||||||
|
{
|
||||||
|
var response = context.ActionContext.HttpContext.Response;
|
||||||
|
response.ContentType = ContentType + ";charset=utf-8";
|
||||||
|
await response.WriteAsync(context.Object.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>ac9be567-540e-4c70-90c2-aaf021307a80</ProjectGuid>
|
||||||
|
<RootNamespace>FormatFilterWebSite</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>51135</DevelopmentServerPort>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FormatFilterWebSite
|
||||||
|
{
|
||||||
|
public class Product
|
||||||
|
{
|
||||||
|
public int SampleInt { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "SampleInt:" + SampleInt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace FormatFilterWebSite
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var configuration = app.GetTestConfiguration();
|
||||||
|
|
||||||
|
app.UseServices(services =>
|
||||||
|
{
|
||||||
|
services.AddMvc(configuration);
|
||||||
|
services.Configure<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
var formatFilter = new FormatFilterAttribute();
|
||||||
|
options.Filters.Add(formatFilter);
|
||||||
|
|
||||||
|
var customFormatter = new CustomFormatter("application/custom");
|
||||||
|
options.OutputFormatters.Add(customFormatter);
|
||||||
|
|
||||||
|
options.FormatterMappings.SetFormatMapping(
|
||||||
|
"custom",
|
||||||
|
MediaTypeHeaderValue.Parse("application/custom"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseMvc(routes =>
|
||||||
|
{
|
||||||
|
routes.MapRoute("formatroute",
|
||||||
|
"{controller}/{action}/{id}.{format?}",
|
||||||
|
new { controller = "Home", action = "Index" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
|
||||||
|
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Kestrel": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
|
||||||
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"aspnet50": { },
|
||||||
|
"aspnetcore50": { }
|
||||||
|
},
|
||||||
|
"webroot": "wwwroot"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue