Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Created at" responses should not have a response body #15590

Merged
merged 1 commit into from Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.DependencyInjection;

namespace Umbraco.Cms.Api.Common.Mvc.ActionResults;

/// <summary>
/// A "created at" action result with no response body.
/// </summary>
public sealed class EmptyCreatedAtActionResult : ActionResult
{
private readonly string? _actionName;
private readonly string? _controllerName;
private readonly object? _routeValues;

public EmptyCreatedAtActionResult(string? actionName, string? controllerName, object? routeValues)
{
_actionName = actionName;
_controllerName = controllerName;
_routeValues = routeValues;
}

public override void ExecuteResult(ActionContext context)
{
ArgumentNullException.ThrowIfNull(context);

HttpRequest request = context.HttpContext.Request;
IUrlHelper urlHelper = context.HttpContext.RequestServices.GetRequiredService<IUrlHelperFactory>().GetUrlHelper(context);

var url = urlHelper.Action(
_actionName,
_controllerName,
_routeValues,
request.Scheme,
request.Host.ToUriComponent());

if (string.IsNullOrEmpty(url))
{
throw new InvalidOperationException("No routes could be found that matched the provided route components");
}

context.HttpContext.Response.StatusCode = StatusCodes.Status201Created;
context.HttpContext.Response.Headers.Location = url;
}
}
Expand Up @@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Common.Filters;
using Umbraco.Cms.Api.Common.Mvc.ActionResults;
using Umbraco.Cms.Api.Management.DependencyInjection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Features;
Expand All @@ -18,10 +19,10 @@ namespace Umbraco.Cms.Api.Management.Controllers;
[JsonOptionsName(Constants.JsonOptionsNames.BackOffice)]
public abstract class ManagementApiControllerBase : Controller, IUmbracoFeature
{
protected CreatedAtActionResult CreatedAtAction<T>(Expression<Func<T, string>> action, Guid id)
protected IActionResult CreatedAtAction<T>(Expression<Func<T, string>> action, Guid id)
=> CreatedAtAction(action, new { id = id });

protected CreatedAtActionResult CreatedAtAction<T>(Expression<Func<T, string>> action, object routeValues)
protected IActionResult CreatedAtAction<T>(Expression<Func<T, string>> action, object routeValues)
{
if (action.Body is not ConstantExpression constantExpression)
{
Expand All @@ -31,10 +32,10 @@ protected CreatedAtActionResult CreatedAtAction<T>(Expression<Func<T, string>> a
var controllerName = ManagementApiRegexes.ControllerTypeToNameRegex().Replace(typeof(T).Name, string.Empty);
var actionName = constantExpression.Value?.ToString() ?? throw new ArgumentException("Expression does not have a value.");

return base.CreatedAtAction(actionName, controllerName, routeValues, null);
return new EmptyCreatedAtActionResult(actionName, controllerName, routeValues);
}

protected CreatedAtActionResult CreatedAtAction<T>(Expression<Func<T, string>> action, string name)
protected IActionResult CreatedAtAction<T>(Expression<Func<T, string>> action, string name)
{
if (action.Body is not ConstantExpression constantExpression)
{
Expand All @@ -44,7 +45,7 @@ protected CreatedAtActionResult CreatedAtAction<T>(Expression<Func<T, string>> a
var controllerName = ManagementApiRegexes.ControllerTypeToNameRegex().Replace(typeof(T).Name, string.Empty);
var actionName = constantExpression.Value?.ToString() ?? throw new ArgumentException("Expression does not have a value.");

return base.CreatedAtAction(actionName, controllerName, new { name = name }, null);
return new EmptyCreatedAtActionResult(actionName, controllerName, new { name = name });
}

protected static Guid CurrentUserKey(IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
Expand Down