Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Analog of CreatedAtAction for update operation

Sometime I allow to create/update the status of IoT devices using the raw data. It means that the client can read the device device state as an array of bytes and send that data via API. The data parsed by the server and send back as a regular DTO.

For create I might introduce the following CreateStatusFromRawData method:

    [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})
    }

I would like to make the same for update operation:

    [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})
    }

How might UpdatedAtAction method implementation look like? So I want actually 3 things:

  1. Return status 200
  2. Get back updated status DTO
  3. Provide correct location header for getting status later via GET method
like image 937
ie. Avatar asked Dec 24 '18 09:12

ie.


1 Answers

You could implement your own UpdatedAtAction like CreatedAtAction.

  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;
            }
    
        }
    }
    
  2. MyControllerBase

    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);
    }
    
  3. Useage

    [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 });
        }
    }
    
like image 115
Edward Avatar answered Sep 27 '22 16:09

Edward