用于更新操作的 CreatedAtAction 模拟

ie.*_*ie. 5 .net c# rest .net-core asp.net-core-webapi

有时我允许使用原始数据创建/更新物联网设备的状态。这意味着客户端可以将设备设备状态作为字节数组读取并通过 API 发送该数据。服务器解析的数据并作为常规 DTO 发回。

对于创建,我可能会引入以下CreateStatusFromRawData方法:

    [HttpGet("{id}/status")]
    [ProducesResponseType(200, Type = typeof(DeviceStatus))]
    [ProducesResponseType(404)]
    public async Task<IActionResult> GetStatus(Guid id)
    {
        // gets the device status
    }

    [HttpPost("{id}/status/rawdata")]
    [ProducesResponseType(201, Type = typeof(DeviceStatus))]
    [ProducesResponseType(404)]
    public async Task<IActionResult> CreateStatusFromRawData(Guid id, [FromBody]byte[] rawdata)
    {
        // some parsing logic
        return CreatedAtAction(nameof(GetStatus), new {id})
    }
Run Code Online (Sandbox Code Playgroud)

我想为更新操作做同样的事情:

    [HttpPut("{id}/status/rawdata")]
    [ProducesResponseType(200, Type = typeof(DeviceStatus))]
    [ProducesResponseType(404)]
    public async Task<IActionResult> UpdateStatusFromRawData(Guid id, [FromBody]byte[] rawdata)
    {
        // some parsing logic
        return **UpdatedAtAction**(nameof(GetStatus), new {id})
    }
Run Code Online (Sandbox Code Playgroud)

UpdatedAtAction 方法的实现会是什么样子?所以我实际上想要三件事:

  1. 返回状态 200
  2. 取回更新的状态 DTO
  3. 提供正确的位置标头,以便稍后通过 GET 方法获取状态

Tao*_*hou 5

你可以UpdatedAtActionCreatedAtAction一样实现你自己的。

  1. UpdatedAtActionResult

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Routing;
    using Microsoft.AspNetCore.Routing;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Net.Http.Headers;
    using System;
    
    namespace MVCPro.CustomResult
    {
        public class UpdatedAtActionResult : ObjectResult
        {
            private const int DefaultStatusCode = StatusCodes.Status200OK;
            public UpdatedAtActionResult(
                string actionName,
                string controllerName,
                object routeValues,
                object value)
                : base(value)
            {
                ActionName = actionName;
                ControllerName = controllerName;
                RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues);
                StatusCode = DefaultStatusCode;
            }
    
            /// <summary>
            /// Gets or sets the <see cref="IUrlHelper" /> used to generate URLs.
            /// </summary>
            public IUrlHelper UrlHelper { get; set; }
    
            /// <summary>
            /// Gets or sets the name of the action to use for generating the URL.
            /// </summary>
            public string ActionName { get; set; }
    
            /// <summary>
            /// Gets or sets the name of the controller to use for generating the URL.
            /// </summary>
            public string ControllerName { get; set; }
    
            /// <summary>
            /// Gets or sets the route data to use for generating the URL.
            /// </summary>
            public RouteValueDictionary RouteValues { get; set; }
    
            /// <inheritdoc />
            public override void OnFormatting(ActionContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }
    
                base.OnFormatting(context);
    
                var request = context.HttpContext.Request;
    
                var urlHelper = UrlHelper;
                if (urlHelper == null)
                {
                    var services = context.HttpContext.RequestServices;
                    urlHelper = services.GetRequiredService<IUrlHelperFactory>().GetUrlHelper(context);
                }
    
                var url = urlHelper.Action(
                    ActionName,
                    ControllerName,
                    RouteValues,
                    request.Scheme,
                    request.Host.ToUriComponent());
    
                if (string.IsNullOrEmpty(url))
                {
                    throw new InvalidOperationException("NoRoutesMatched");
                }
    
                context.HttpContext.Response.Headers[HeaderNames.Location] = url;
            }
    
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我的控制器库

    public class MyControllerBase: Controller
    {
        [NonAction]
        public virtual UpdatedAtActionResult UpdatedAtAction(string actionName, object value)
        => UpdatedAtAction(actionName, routeValues: null, value: value);
    
        [NonAction]
        public virtual UpdatedAtActionResult UpdatedAtAction(string actionName, object routeValues, object value)
                => UpdatedAtAction(actionName, controllerName: null, routeValues: routeValues, value: value);
    
        [NonAction]
        public virtual UpdatedAtActionResult UpdatedAtAction(
                        string actionName,
                        string controllerName,
                        object routeValues,
                        object value)
                        => new UpdatedAtActionResult(actionName, controllerName, routeValues, value);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 用途

    [Route("api/User")]
    public class UserApiController : MyControllerBase
    {
        [HttpGet("{id}/status")]
        [ProducesResponseType(200, Type = typeof(DeviceStatus))]
        [ProducesResponseType(404)]
        public async Task<IActionResult> GetStatus(Guid id)
        {
            // gets the device status
            return Ok(new DeviceStatus { DeviceId = id });
        }
    
        [HttpPost("{id}/status/rawdata")]
        [ProducesResponseType(201, Type = typeof(DeviceStatus))]
        [ProducesResponseType(404)]
        public async Task<IActionResult> CreateStatusFromRawData(Guid id, [FromBody]byte[] rawdata)
        {
            // some parsing logic
            return CreatedAtAction(nameof(GetStatus), new { id });
        }
        [HttpPut("{id}/status/rawdata")]
        [ProducesResponseType(200, Type = typeof(DeviceStatus))]
        [ProducesResponseType(404)]
        public async Task<IActionResult> UpdateStatusFromRawData(Guid id, [FromBody]byte[] rawdata)
        {
            // some parsing logic
            return UpdatedAtAction(nameof(GetStatus), new { id });
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)