michasaucer
michasaucer

Reputation: 5238

Overwrite request object in ASP .NET Core

I have base class for every request in my app:

public abstract class BaseDto
{
   public string Uid { get; set; }
}

public class RequestDto : BaseDto
{
    public string SomeData { get; set; }
}

Im using my ReuqestDto class in my controller actions:

[HttpGet]
public IEnumerable<string> Get(RequestDto req)
{
    // some logic on request
    if (req.Uid != null)
    {
        // perform action
    }

}

The user passing only SomeData property to me. In my JWT Token i have saved some information about Uid for BaseDto. What is the best way to write data to Uid using middleware/filter to have that information in my Get() method? I Tried to serialized HttpContext.Request.Body but not success because i cant find, how to do it properly. Or maybe there are better solutions for this problem? How to write data to my incoming objects in app?

Upvotes: 0

Views: 914

Answers (2)

KarateJB
KarateJB

Reputation: 961

I often created a filter which implements Attribute and IAsyncActionFilter to get the information before go inside the Controller's action.

Here is an example,

using System.IdentityModel.Tokens.Jwt;

 public class UserProfileFilter : Attribute, IAsyncActionFilter
 {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            string uid = string.Empty;
            StringValues authHeaderVal = default(StringValues);

            // Get UID from JWT
            if (context.HttpContext.Request.Headers.TryGetValue("Authorization", out authHeaderVal))
            {
                string bearerTokenPrefix = "Bearer";
                string accessToken = string.Empty;
                string authHeaderStr = authHeaderVal.ToString();
                if (!string.IsNullOrEmpty(authHeaderStr) && authHeaderStr.StartsWith(bearerTokenPrefix, StringComparison.OrdinalIgnoreCase))
                {
                    accessToken = authHeaderStr.Replace(bearerTokenPrefix, string.Empty, StringComparison.OrdinalIgnoreCase).Trim();
                }

                var handler = new JwtSecurityTokenHandler();
                var token = handler.ReadJwtToken(accessToken);
                uid = token.Claims.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value;
            }

            // Or Get UID from ActionExecutingContext
            var user = context.HttpContext.User;
            if (user.Identity.IsAuthenticated)
            {
                uid = user.Claims.FirstOrDefault(c => c.Type.Equals("sub", StringComparison.OrdinalIgnoreCase))?.Value;
            }

            // Get payload
            RequestDto payload = (RequestDto)context.ActionArguments?.Values.FirstOrDefault(v => v is RequestDto);
            payload.Uid = uid;

            await next();
        }
}

And then you can put the filter on any action.

[HttpPost]
[Authorize]
[TypeFilter(typeof(UserProfileFilter))]
public ActionResult<IActionResult> AdminGet(RequestDto request)
{           
   Debug.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(request));
   return this.Ok();
}

The above filter will use the sub claim's value to overwrite the value of the incoming payload.

For example, if I post the payload as following,

{
    "uid" : "",
    "someData": "Test"
}

The action will finally output {"Uid":"MyID","SomeData":"Test"}.

Upvotes: 1

daremachine
daremachine

Reputation: 2788

This is probably what you want.

You should to create own interface for models like that

public interface IMyRequestType { }

Your model should implement it for finding model in FilterAttribute

public class MyModel : IMyRequestType
{
    public string ID { get; set; }
}

And create your filter attribute with OnActionExecuting implentation

public class MyFilterAttribute : TypeFilterAttribute
{
    public MyFilterAttribute() : base(typeof(MyFilterImpl)) { }

    private class MyFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;

        public MyFilterAttributeImpl(ILoggerFactory loggerFactory)
        {
            // get something from DI
            _logger = loggerFactory.CreateLogger<MyFilterAttributeImpl>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            // get your request model
            var model = context.ActionArguments.Values.OfType<IMyRequestType>().Single();

            // get your key
            //context.HttpContext.User or whatever

            // do something with model
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // perform some logic work
        }
    }
}

Upvotes: 1

Related Questions