Home > other >  Action filter that sets language globally in ASP.NET Core using C#
Action filter that sets language globally in ASP.NET Core using C#

Time:08-03

I'm trying to create an ActionFilter that will read the Accept-Language header and find if it matches any value in locale and if it does not match, use the a default value which is "en".

First of all, this is my handler:

using MediatR;
using Microsoft.EntityFrameworkCore;
using Subscription.Domain.Common;
using Subscription.Domain.Exceptions;
using Subscription.Infrastructure.Configuration;
using System.Net;

namespace Subscription.API.Application.Package.Queries.Get
{
    public class GetHandler : IRequestHandler<GetRequest, EntityResponseModel>
    {
        private readonly SubscriptionContext _context;
        private readonly IHttpContextAccessor _httpContext;

        public GetHandler(SubscriptionContext context, IHttpContextAccessor httpContext)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _httpContext = httpContext;
        }

        public async Task<EntityResponseModel> Handle(GetRequest request, CancellationToken cancellationToken)
        {
            var lang = _httpContext.HttpContext.Request.Headers["Accept-Language"].ToString();

            var packages = await _context.Packages.Where(x => !x.IsDeleted)
                .Select(x => new GetResponseModel()
                {
                    Id = x.Id,
                    BrandId = x.BrandId,
                    StartAt = x.StartAt,
                    IconUrl = x.IconUrl,
                    ImageUrl = x.ImageUrl,
                    CreatedAt = x.CreatedDate,
                    Title = x.PackageTranslations.FirstOrDefault(pt => pt.PackageId == x.Id && pt.Locale.Equals(lang)).Title,
                    Description = x.PackageTranslations.FirstOrDefault(pt => pt.PackageId == x.Id && pt.Locale.Equals(lang)).Description

                }).ToListAsync();

            if (!packages.Any())
            {
                throw new DomainException(HttpStatusCode.NotFound, "No record found.");
            }

            return new EntityResponseModel()
            {
                Data = packages
            };
        }
    }
}

And this is the controller:

using Microsoft.AspNetCore.Mvc;
using MediatR;
using Subscription.API.Application.Package.Queries.Get;
using Subscription.API.Application.Bundle.Queries.GetById;
using Subscription.API.Filters;

namespace Subscription.API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class PackagesController : ControllerBase
    {
        private readonly IMediator _mediator;

        public PackagesController(IMediator mediator)
        {
            _mediator = mediator;
        }

        [HttpGet]
        [ValidateHeaders]
        public async Task<ActionResult> GetAll()
        {
            var response = await _mediator.Send(new GetRequest() {  });

            return Ok(response);
        }
    }
}

This the filter class:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Subscription.API.Filters
{
    public class ValidateHeaders : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var headers = context.HttpContext.Request.Headers;
            // need to do something here
        }
    }
}

So, basically what I need is to create a filter that:

  1. Check the Accept-Language header if it have a value that matches locale
  2. If it does not have a value that matches it, return string with the default value which is "en"

Any idea what I should do?

CodePudding user response:

You can do this in 2 different ways;

  • Action Filter
  • Middleware

1.Action Filter

If you use this action filter, you have to write this attribute on each endpoint.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Subscription.API.Filters
{
    public class ValidateHeaders : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var languageHeader = context.HttpContext.Request.Headers.AcceptLanguage;

            if (string.IsNullOrWhiteSpace(languageHeader))
               context.HttpContext.Request.Headers.AcceptLanguage = "en";
        }
    }
}

2.Middleware

If you write it as middleware, it will be enough to place this middleware in the first place in the request pipeline.

public class ValidateHeadersMiddleware
{
    private readonly RequestDelegate _next;

    public ValidateHeadersMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        var languageHeader = context.Request.Headers.AcceptLanguage;

        if (string.IsNullOrWhiteSpace(languageHeader))
            context.Request.Headers.AcceptLanguage = "en";

        //Continue processing
        if (_next != null)
            await _next.Invoke(context);      
    }
}

This will effect all requests.

public static class MiddlewareExtension
{
    public static IApplicationBuilder UseHeaderValidation(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<ValidateHeadersMiddleware>();
    }
}

In the Configure method in Startup.cs or minimal api configuration in Program.cs, use this middleware;

...
  
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHeaderValidation();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

...
  • Related