Fixing tests, removing old files, adds notes
This commit is contained in:
@@ -144,7 +144,7 @@ namespace Umbraco.Tests.Integration.TestServerTest.Controllers
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode);
|
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
Assert.AreEqual(")]}',\n{\"Message\":\"No variants flagged for saving\"}", body);
|
Assert.AreEqual(AngularJsonMediaTypeFormatter.XsrfPrefix + "{\"Message\":\"No variants flagged for saving\"}", body);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
@@ -11,20 +10,18 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
|||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.TestHost;
|
using Microsoft.AspNetCore.TestHost;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.Hosting;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Umbraco.Core;
|
using Umbraco.Core;
|
||||||
|
using Umbraco.Core.Cache;
|
||||||
|
using Umbraco.Core.DependencyInjection;
|
||||||
using Umbraco.Extensions;
|
using Umbraco.Extensions;
|
||||||
using Umbraco.Tests.Integration.Testing;
|
using Umbraco.Tests.Integration.Testing;
|
||||||
using Umbraco.Tests.Testing;
|
using Umbraco.Tests.Testing;
|
||||||
using Umbraco.Web;
|
using Umbraco.Web;
|
||||||
using Umbraco.Core.DependencyInjection;
|
|
||||||
using Umbraco.Web.Common.Controllers;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Umbraco.Core.Cache;
|
|
||||||
using Umbraco.Core.Persistence;
|
|
||||||
using Umbraco.Core.Runtime;
|
|
||||||
using Umbraco.Web.BackOffice.Controllers;
|
using Umbraco.Web.BackOffice.Controllers;
|
||||||
|
using Umbraco.Web.Common.Controllers;
|
||||||
|
using Umbraco.Web.Website.Controllers;
|
||||||
|
|
||||||
namespace Umbraco.Tests.Integration.TestServerTest
|
namespace Umbraco.Tests.Integration.TestServerTest
|
||||||
{
|
{
|
||||||
@@ -133,8 +130,14 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
|||||||
public override void ConfigureServices(IServiceCollection services)
|
public override void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddTransient<TestUmbracoDatabaseFactoryProvider>();
|
services.AddTransient<TestUmbracoDatabaseFactoryProvider>();
|
||||||
var typeLoader = services.AddTypeLoader(GetType().Assembly, TestHelper.GetWebHostEnvironment(), TestHelper.GetHostingEnvironment(),
|
var typeLoader = services.AddTypeLoader(
|
||||||
TestHelper.ConsoleLoggerFactory, AppCaches.NoCache, Configuration, TestHelper.Profiler);
|
GetType().Assembly,
|
||||||
|
TestHelper.GetWebHostEnvironment(),
|
||||||
|
TestHelper.GetHostingEnvironment(),
|
||||||
|
TestHelper.ConsoleLoggerFactory,
|
||||||
|
AppCaches.NoCache,
|
||||||
|
Configuration,
|
||||||
|
TestHelper.Profiler);
|
||||||
|
|
||||||
var builder = new UmbracoBuilder(services, Configuration, typeLoader);
|
var builder = new UmbracoBuilder(services, Configuration, typeLoader);
|
||||||
|
|
||||||
@@ -147,10 +150,16 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
|||||||
.AddBackOfficeIdentity()
|
.AddBackOfficeIdentity()
|
||||||
.AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme)
|
.AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme)
|
||||||
.AddPreviewSupport()
|
.AddPreviewSupport()
|
||||||
//.WithMiniProfiler() // we don't want this running in tests
|
|
||||||
.AddMvcAndRazor(mvcBuilding: mvcBuilder =>
|
.AddMvcAndRazor(mvcBuilding: mvcBuilder =>
|
||||||
{
|
{
|
||||||
|
// Adds Umbraco.Web.BackOffice
|
||||||
mvcBuilder.AddApplicationPart(typeof(ContentController).Assembly);
|
mvcBuilder.AddApplicationPart(typeof(ContentController).Assembly);
|
||||||
|
|
||||||
|
// Adds Umbraco.Web.Common
|
||||||
|
mvcBuilder.AddApplicationPart(typeof(RenderController).Assembly);
|
||||||
|
|
||||||
|
// Adds Umbraco.Web.Website
|
||||||
|
mvcBuilder.AddApplicationPart(typeof(SurfaceController).Assembly);
|
||||||
})
|
})
|
||||||
.AddWebServer()
|
.AddWebServer()
|
||||||
.Build();
|
.Build();
|
||||||
@@ -159,6 +168,8 @@ namespace Umbraco.Tests.Integration.TestServerTest
|
|||||||
public override void Configure(IApplicationBuilder app)
|
public override void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.UseUmbraco();
|
app.UseUmbraco();
|
||||||
|
app.UseUmbracoBackOffice();
|
||||||
|
app.UseUmbracoWebsite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Web.Http;
|
|
||||||
using Microsoft.Owin.Testing;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using Umbraco.Core;
|
|
||||||
using Umbraco.Web;
|
|
||||||
using Umbraco.Web.WebApi;
|
|
||||||
|
|
||||||
namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
|
||||||
{
|
|
||||||
public class TestRunner
|
|
||||||
{
|
|
||||||
private readonly Func<HttpRequestMessage, IUmbracoContextAccessor, ApiController> _controllerFactory;
|
|
||||||
|
|
||||||
public TestRunner(Func<HttpRequestMessage, IUmbracoContextAccessor, ApiController> controllerFactory)
|
|
||||||
{
|
|
||||||
_controllerFactory = controllerFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Tuple<HttpResponseMessage, string>> Execute(string controllerName, string actionName, HttpMethod method,
|
|
||||||
HttpContent content = null,
|
|
||||||
MediaTypeWithQualityHeaderValue mediaTypeHeader = null,
|
|
||||||
bool assertOkResponse = true, object routeDefaults = null, string url = null)
|
|
||||||
{
|
|
||||||
if (mediaTypeHeader == null)
|
|
||||||
{
|
|
||||||
mediaTypeHeader = new MediaTypeWithQualityHeaderValue("application/json");
|
|
||||||
}
|
|
||||||
if (routeDefaults == null)
|
|
||||||
{
|
|
||||||
routeDefaults = new { controller = controllerName, action = actionName, id = RouteParameter.Optional };
|
|
||||||
}
|
|
||||||
|
|
||||||
var startup = new TestStartup(
|
|
||||||
configuration =>
|
|
||||||
{
|
|
||||||
configuration.Routes.MapHttpRoute("Default",
|
|
||||||
routeTemplate: "{controller}/{action}/{id}",
|
|
||||||
defaults: routeDefaults);
|
|
||||||
},
|
|
||||||
_controllerFactory);
|
|
||||||
|
|
||||||
using (var server = TestServer.Create(builder => startup.Configuration(builder)))
|
|
||||||
{
|
|
||||||
var request = new HttpRequestMessage
|
|
||||||
{
|
|
||||||
RequestUri = new Uri("https://testserver/" + (url ?? "")),
|
|
||||||
Method = method
|
|
||||||
};
|
|
||||||
|
|
||||||
if (content != null)
|
|
||||||
request.Content = content;
|
|
||||||
|
|
||||||
request.Headers.Accept.Add(mediaTypeHeader);
|
|
||||||
|
|
||||||
Console.WriteLine(request);
|
|
||||||
var response = await server.HttpClient.SendAsync(request);
|
|
||||||
Console.WriteLine(response);
|
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode == false)
|
|
||||||
{
|
|
||||||
WriteResponseError(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = (await ((StreamContent)response.Content).ReadAsStringAsync()).TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix);
|
|
||||||
if (!json.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
var deserialized = JsonConvert.DeserializeObject(json);
|
|
||||||
Console.Write(JsonConvert.SerializeObject(deserialized, Formatting.Indented));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assertOkResponse)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Tuple.Create(response, json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteResponseError(HttpResponseMessage response)
|
|
||||||
{
|
|
||||||
var result = response.Content.ReadAsStringAsync().Result;
|
|
||||||
Console.Out.WriteLine("Http operation unsuccessfull");
|
|
||||||
Console.Out.WriteLine($"Status: '{response.StatusCode}'");
|
|
||||||
Console.Out.WriteLine($"Reason: '{response.ReasonPhrase}'");
|
|
||||||
Console.Out.WriteLine(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -174,7 +174,6 @@
|
|||||||
<Compile Include="TestHelpers\ControllerTesting\SpecificAssemblyResolver.cs" />
|
<Compile Include="TestHelpers\ControllerTesting\SpecificAssemblyResolver.cs" />
|
||||||
<Compile Include="TestHelpers\ControllerTesting\TestControllerActivator.cs" />
|
<Compile Include="TestHelpers\ControllerTesting\TestControllerActivator.cs" />
|
||||||
<Compile Include="TestHelpers\ControllerTesting\TestControllerActivatorBase.cs" />
|
<Compile Include="TestHelpers\ControllerTesting\TestControllerActivatorBase.cs" />
|
||||||
<Compile Include="TestHelpers\ControllerTesting\TestRunner.cs" />
|
|
||||||
<Compile Include="TestHelpers\ControllerTesting\TestStartup.cs" />
|
<Compile Include="TestHelpers\ControllerTesting\TestStartup.cs" />
|
||||||
<Compile Include="TestHelpers\ControllerTesting\TraceExceptionLogger.cs" />
|
<Compile Include="TestHelpers\ControllerTesting\TraceExceptionLogger.cs" />
|
||||||
<Compile Include="Testing\Objects\Accessors\NoHttpContextAccessor.cs" />
|
<Compile Include="Testing\Objects\Accessors\NoHttpContextAccessor.cs" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -636,19 +636,20 @@ namespace Umbraco.Web.BackOffice.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves content
|
/// Saves content
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
|
||||||
[FileUploadCleanupFilter]
|
[FileUploadCleanupFilter]
|
||||||
[ContentSaveValidation]
|
[ContentSaveValidation]
|
||||||
public async Task<ContentItemDisplay> PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem)
|
public async Task<ContentItemDisplay> PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem)
|
||||||
{
|
{
|
||||||
var contentItemDisplay = await PostSaveInternal(contentItem,
|
var contentItemDisplay = await PostSaveInternal(
|
||||||
|
contentItem,
|
||||||
content =>
|
content =>
|
||||||
{
|
{
|
||||||
EnsureUniqueName(content.Name, content, "Name");
|
EnsureUniqueName(content.Name, content, "Name");
|
||||||
|
|
||||||
_contentService.SaveBlueprint(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id);
|
_contentService.SaveBlueprint(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id);
|
||||||
//we need to reuse the underlying logic so return the result that it wants
|
|
||||||
return OperationResult.Succeed(new EventMessages());
|
// we need to reuse the underlying logic so return the result that it wants
|
||||||
|
return OperationResult.Succeed(new EventMessages());
|
||||||
},
|
},
|
||||||
content =>
|
content =>
|
||||||
{
|
{
|
||||||
@@ -663,7 +664,6 @@ namespace Umbraco.Web.BackOffice.Controllers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves content
|
/// Saves content
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
|
||||||
[FileUploadCleanupFilter]
|
[FileUploadCleanupFilter]
|
||||||
[ContentSaveValidation]
|
[ContentSaveValidation]
|
||||||
[OutgoingEditorModelEvent]
|
[OutgoingEditorModelEvent]
|
||||||
@@ -679,9 +679,9 @@ namespace Umbraco.Web.BackOffice.Controllers
|
|||||||
|
|
||||||
private async Task<ContentItemDisplay> PostSaveInternal(ContentItemSave contentItem, Func<IContent, OperationResult> saveMethod, Func<IContent, ContentItemDisplay> mapToDisplay)
|
private async Task<ContentItemDisplay> PostSaveInternal(ContentItemSave contentItem, Func<IContent, OperationResult> saveMethod, Func<IContent, ContentItemDisplay> mapToDisplay)
|
||||||
{
|
{
|
||||||
//Recent versions of IE/Edge may send in the full client side file path instead of just the file name.
|
// Recent versions of IE/Edge may send in the full client side file path instead of just the file name.
|
||||||
//To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all
|
// To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all
|
||||||
//uploaded files to being *only* the actual file name (as it should be).
|
// uploaded files to being *only* the actual file name (as it should be).
|
||||||
if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any())
|
if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any())
|
||||||
{
|
{
|
||||||
foreach (var file in contentItem.UploadedFiles)
|
foreach (var file in contentItem.UploadedFiles)
|
||||||
@@ -690,7 +690,7 @@ namespace Umbraco.Web.BackOffice.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we've reached here it means:
|
// If we've reached here it means:
|
||||||
// * Our model has been bound
|
// * Our model has been bound
|
||||||
// * and validated
|
// * and validated
|
||||||
// * any file attachments have been saved to their temporary location for us to use
|
// * any file attachments have been saved to their temporary location for us to use
|
||||||
@@ -700,20 +700,20 @@ namespace Umbraco.Web.BackOffice.Controllers
|
|||||||
|
|
||||||
var passesCriticalValidationRules = ValidateCriticalData(contentItem, out var variantCount);
|
var passesCriticalValidationRules = ValidateCriticalData(contentItem, out var variantCount);
|
||||||
|
|
||||||
//we will continue to save if model state is invalid, however we cannot save if critical data is missing.
|
// we will continue to save if model state is invalid, however we cannot save if critical data is missing.
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
{
|
{
|
||||||
//check for critical data validation issues, we can't continue saving if this data is invalid
|
// check for critical data validation issues, we can't continue saving if this data is invalid
|
||||||
if (!passesCriticalValidationRules)
|
if (!passesCriticalValidationRules)
|
||||||
{
|
{
|
||||||
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
|
// ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
|
||||||
// add the model state to the outgoing object and throw a validation message
|
// add the model state to the outgoing object and throw a validation message
|
||||||
var forDisplay = mapToDisplay(contentItem.PersistedContent);
|
var forDisplay = mapToDisplay(contentItem.PersistedContent);
|
||||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||||
throw HttpResponseException.CreateValidationErrorResponse(forDisplay);
|
throw HttpResponseException.CreateValidationErrorResponse(forDisplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if there's only one variant and the model state is not valid we cannot publish so change it to save
|
// if there's only one variant and the model state is not valid we cannot publish so change it to save
|
||||||
if (variantCount == 1)
|
if (variantCount == 1)
|
||||||
{
|
{
|
||||||
switch (contentItem.Action)
|
switch (contentItem.Action)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -29,14 +29,12 @@ namespace Umbraco.Web.BackOffice.Controllers
|
|||||||
[JsonDateTimeFormat]
|
[JsonDateTimeFormat]
|
||||||
public abstract class ContentControllerBase : BackOfficeNotificationsController
|
public abstract class ContentControllerBase : BackOfficeNotificationsController
|
||||||
{
|
{
|
||||||
protected ICultureDictionary CultureDictionary { get; }
|
|
||||||
protected ILoggerFactory LoggerFactory { get; }
|
|
||||||
protected IShortStringHelper ShortStringHelper { get; }
|
|
||||||
protected IEventMessagesFactory EventMessages { get; }
|
|
||||||
protected ILocalizedTextService LocalizedTextService { get; }
|
|
||||||
private readonly ILogger<ContentControllerBase> _logger;
|
private readonly ILogger<ContentControllerBase> _logger;
|
||||||
private readonly IJsonSerializer _serializer;
|
private readonly IJsonSerializer _serializer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ContentControllerBase"/> class.
|
||||||
|
/// </summary>
|
||||||
protected ContentControllerBase(
|
protected ContentControllerBase(
|
||||||
ICultureDictionary cultureDictionary,
|
ICultureDictionary cultureDictionary,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
@@ -54,6 +52,31 @@ namespace Umbraco.Web.BackOffice.Controllers
|
|||||||
_serializer = serializer;
|
_serializer = serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="ICultureDictionary"/>
|
||||||
|
/// </summary>
|
||||||
|
protected ICultureDictionary CultureDictionary { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="ILoggerFactory"/>
|
||||||
|
/// </summary>
|
||||||
|
protected ILoggerFactory LoggerFactory { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IShortStringHelper"/>
|
||||||
|
/// </summary>
|
||||||
|
protected IShortStringHelper ShortStringHelper { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IEventMessagesFactory"/>
|
||||||
|
/// </summary>
|
||||||
|
protected IEventMessagesFactory EventMessages { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="ILocalizedTextService"/>
|
||||||
|
/// </summary>
|
||||||
|
protected ILocalizedTextService LocalizedTextService { get; }
|
||||||
|
|
||||||
protected NotFoundObjectResult HandleContentNotFound(object id, bool throwException = true)
|
protected NotFoundObjectResult HandleContentNotFound(object id, bool throwException = true)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("id", $"content with id: {id} was not found");
|
ModelState.AddModelError("id", $"content with id: {id} was not found");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
@@ -34,6 +34,7 @@ namespace Umbraco.Web.Common.Filters
|
|||||||
_arrayPool = arrayPool;
|
_arrayPool = arrayPool;
|
||||||
_options = options;
|
_options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnResultExecuted(ResultExecutedContext context)
|
public void OnResultExecuted(ResultExecutedContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,8 +192,6 @@
|
|||||||
<Compile Include="Mvc\EnsurePublishedContentRequestAttribute.cs" />
|
<Compile Include="Mvc\EnsurePublishedContentRequestAttribute.cs" />
|
||||||
<Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" />
|
<Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" />
|
||||||
<Compile Include="Security\AuthenticationOptionsExtensions.cs" />
|
<Compile Include="Security\AuthenticationOptionsExtensions.cs" />
|
||||||
<Compile Include="WebApi\AngularJsonMediaTypeFormatter.cs" />
|
|
||||||
<Compile Include="WebApi\AngularJsonOnlyConfigurationAttribute.cs" />
|
|
||||||
<Compile Include="PublishedPropertyExtension.cs" />
|
<Compile Include="PublishedPropertyExtension.cs" />
|
||||||
<Compile Include="Mvc\MergeParentContextViewDataAttribute.cs" />
|
<Compile Include="Mvc\MergeParentContextViewDataAttribute.cs" />
|
||||||
<Compile Include="Mvc\ViewDataDictionaryExtensions.cs" />
|
<Compile Include="Mvc\ViewDataDictionaryExtensions.cs" />
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Formatting;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Umbraco.Core.Logging;
|
|
||||||
|
|
||||||
namespace Umbraco.Web.WebApi
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This will format the JSON output for use with AngularJs's approach to JSON Vulnerability attacks
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// See: http://docs.angularjs.org/api/ng.$http (Security considerations)
|
|
||||||
/// </remarks>
|
|
||||||
public class AngularJsonMediaTypeFormatter : JsonMediaTypeFormatter
|
|
||||||
{
|
|
||||||
|
|
||||||
public const string XsrfPrefix = ")]}',\n";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This will prepend the special chars to the stream output that angular will strip
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="writeStream"></param>
|
|
||||||
/// <param name="content"></param>
|
|
||||||
/// <param name="transportContext"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
|
|
||||||
{
|
|
||||||
if (type == null) throw new ArgumentNullException("type");
|
|
||||||
if (writeStream == null) throw new ArgumentNullException("writeStream");
|
|
||||||
|
|
||||||
var effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers);
|
|
||||||
|
|
||||||
using (var streamWriter = new StreamWriter(writeStream, effectiveEncoding,
|
|
||||||
//we are only writing a few chars so we don't need to allocate a large buffer
|
|
||||||
128,
|
|
||||||
//this is important! We don't want to close the stream, the base class is in charge of stream management, we just want to write to it.
|
|
||||||
leaveOpen:true))
|
|
||||||
{
|
|
||||||
//write the special encoding for angular json to the start
|
|
||||||
// (see: http://docs.angularjs.org/api/ng.$http)
|
|
||||||
streamWriter.Write(XsrfPrefix);
|
|
||||||
streamWriter.Flush();
|
|
||||||
await base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http.Formatting;
|
|
||||||
using System.Web.Http.Controllers;
|
|
||||||
|
|
||||||
namespace Umbraco.Web.WebApi
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Applying this attribute to any webapi controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention.
|
|
||||||
/// </summary>
|
|
||||||
public class AngularJsonOnlyConfigurationAttribute : Attribute, IControllerConfiguration
|
|
||||||
{
|
|
||||||
public virtual void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
|
|
||||||
{
|
|
||||||
//remove all json/xml formatters then add our custom one
|
|
||||||
var toRemove = controllerSettings.Formatters.Where(t => (t is JsonMediaTypeFormatter) || (t is XmlMediaTypeFormatter)).ToList();
|
|
||||||
foreach (var r in toRemove)
|
|
||||||
{
|
|
||||||
controllerSettings.Formatters.Remove(r);
|
|
||||||
}
|
|
||||||
controllerSettings.Formatters.Add(new AngularJsonMediaTypeFormatter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user