Best place to set CurrentCulture for multilingual ASP.NET WebAPI applications - asp.net

based on this question (Best place to set CurrentCulture for multilingual ASP.NET MVC web applications) I'm looking for a global way to do the same for ASP.NET WebAPI.
My current implementation for ASP.NET MVC looks like this:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new CultureAwareControllerActivator()));
}
}
public class CultureAwareControllerActivator : System.Web.Mvc.IControllerActivator
{
public System.Web.Mvc.IController Create(System.Web.Routing.RequestContext requestContext, System.Type controllerType)
{
string cultureName = null;
// Attempt to read the culture cookie from Request
HttpCookie cultureCookie = requestContext.HttpContext.Request.Cookies["_culture"];
if (cultureCookie != null)
cultureName = cultureCookie.Value;
else
cultureName = requestContext.HttpContext.Request.UserLanguages != null && requestContext.HttpContext.Request.UserLanguages.Length > 0 ?
requestContext.HttpContext.Request.UserLanguages[0] : // obtain it from HTTP header AcceptLanguages
null;
// Validate culture name
cultureName = RoedingGmbh.Pis2Go.Web.Mvc.Helpers.CultureHelper.GetImplementedCulture(cultureName); // This is safe
// Modify current thread's cultures
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName);
// Currently the UI Language is only english
//Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
return DependencyResolver.Current.GetService(controllerType) as IController;
}
}
I'm trying to do the same things as in ASP.NET MVC, but following code doesn't work properly.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
GlobalConfiguration.Configuration.Services.Replace(typeof(System.Web.Http.Dispatcher.IHttpControllerActivator), new CultureAwareHttpControllerActivator());
}
}
public class CultureAwareHttpControllerActivator : System.Web.Http.Dispatcher.IHttpControllerActivator
{
public System.Web.Http.Controllers.IHttpController Create(System.Net.Http.HttpRequestMessage request, System.Web.Http.Controllers.HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("DE-de");
return GlobalConfiguration.Configuration.DependencyResolver.GetService(controllerType) as System.Web.Http.Controllers.IHttpController;
}
}
With that code, WebAPI cannot find any route anymore :(
What have I to do to get this to work!
Regards,
Daniel

If you want global localization for whole application, you can put the set culture code into the BeginRequest method of the HttpApplication in global asax file. This will set your culture in the most first place of each request and will affect every MVC and webapi request.

I'd use a ActionFilter
If you are using Asp.Net MVC
//A foreigner, has possibly brew a cookie for me
public class SpeakNativeTongueAttribute : ActionFilterAttribute, IActionFilter
{
const string cookieName = "culture";
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
var cookieKeys = filterContext.RequestContext.HttpContext.Request.Cookies.AllKeys;
if (cookieKeys.Contains(cookieName))
{
//eat the cookie
var theCultureCookie = filterContext.RequestContext.HttpContext.Request.Cookies[cookieName];
var theCulture = theCultureCookie.Value;
//say thanks in native tongue
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(theCulture);
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(theCulture);
}
else
{
//Didn't receive a cookie, don't speak their language, those bastards!
}
}
}
In Javascript you can set the language in a cookie named "culture"

Related

Access Session by ActionFilterAttribute in .NET CORE

I tried to access the session-value by using the ActionFilterAttribute in .NET CORE. Therefore i use the following code:
A normal .NET Core Controller which gets the annotation [SessionCheck].
[SessionCheck]
public class HomeController : Controller
{
}
These class is extended by the ActionFilterAttribute Class and overrides the OnActionExecuted Method, where i would like to access the session value. Is there a possibility? I could only access the Key but not the session value.
public class SessionCheck:ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
var session = context.HttpContext.Session;
//var mySessionValue = session["user"];
}
}
My main problem is, that i have a session value which describes where i have to route my request. If there is a special value in, i would like to route my request to Location A, otherwise to Location B.
Is there another possibility to do that?
I have same issue, you can do something like below:
filterContext.HttpContext.Session.GetString("user");
public class SessionCheck : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
if (actionExecutingContext.HttpContext.Session.GetString("user") == "User One")
{
actionExecutingContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "LocationA Controller",
action = "LocationA Action",
returnurl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetEncodedUrl(actionExecutingContext.HttpContext.Request)
}));
}
else
{
actionExecutingContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "LocationB Controller",
action = "LocationB Action",
returnurl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetEncodedUrl(actionExecutingContext.HttpContext.Request)
}));
}
}
}
Write SessionCheck class as above you can set your special value by typing HttpContext.Session.SetString("user", "User One"); in where you want to set also you can use multiple if-elseif like the same, This is working in .Net Core 3.1.

Getting parameterles public constructor error

An error occurred when trying to create a controller of type
'ChatBotController'. Make sure that the controller has a
parameterless public constructor.
at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType) ↵ at
System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage
request) ↵ at
System.Web.Http.Dispatcher.HttpControllerDispatcher.d__15.MoveNext()
When I try to reach my IFeedbackRepository I get the error aboe. It happens when I put in the constructor in my ChatBotController.cs
public class ChatBotController : ApiController
{
IFeedbackRepository _feedbackRepository;
public ChatBotController(IFeedbackRepository feedbackRepository)
{
_feedbackRepository = feedbackRepository;
}
[HttpPost]
public IHttpActionResult PostQuestion([FromBody]string message) //TODO: make sure that webapi will search the message in the body of the http request
{
throw new NotImplementedException();
}
}
I'm using both MVC and Api which I both resolve in my Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
DependencyConfig.RegisterWebApiDependencies();
DependencyConfig.RegisterMvcDependencies();
}
This is my DependencyConfig.cs for both MVC and Api:
public static void RegisterWebApiDependencies()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<IAnswerGenerator, PxlAnswerGenerator>(Lifestyle.Scoped);
container.Register<ChatBotDbContext>(Lifestyle.Scoped);
container.Register<IFeedbackRepository, FeedbackDbRepository>(Lifestyle.Scoped);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
public static void RegisterMvcDependencies()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.Register<IFeedbackRepository, FeedbackDbRepository>(Lifestyle.Scoped);
container.Register<ChatBotDbContext>(Lifestyle.Scoped);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
What am I doing wrong?
According to the documentation of Simple-Injector when you want to initialize the resolver for the WebApi part of your registration you need to call
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorWebApiDependencyResolver(container));
I don't see you calling container.RegisterWebApiControllers(GlobalConfiguration.Configuration); in RegisterWebApiDependencies(). This is required.
You may want to review the simpleinjector documentation for integrating with ASP.NET Web API and MVC here:
https://simpleinjector.readthedocs.io/en/latest/webapiintegration.html
Also the documentation above has the container/DI setup at the beginning of application_start(). If the above change alone does not work, you may want to try putting the following two lines at the start of application_start():
DependencyConfig.RegisterWebApiDependencies();
DependencyConfig.RegisterMvcDependencies();

Owin SuppressDefaultHostAuthentication and existing HTTP Module

In an existing ASP.NET MVC application I've added WebAPI. I've used most of the template code that's generated from a brand new project.
I'm facing some issues concerning authentication. In order to avoid the normal login procedure for the api I've added:
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
as per the default template.
However, I get an error calling my api stating: "No OWIN authentication manager is associated with the request."
I found out that this is caused because there's a custom HTTP module active. Removing the module from the web.config makes the API work. However the 'normal' web application needs this module.
Is there a way to let the HTTP module coexist with the owin authentication?
The Module is defined like this:
public class MGPContextInitializerModule : IHttpModule
{
public void Init(HttpApplication context)
{
if (context != null)
{
context.AcquireRequestState += AcquireRequestState;
context.BeginRequest += new EventHandler(BeginRequest);
}
}
private void AcquireRequestState(object sender, EventArgs e)
{
if (HttpContext.Current.Session != null)
{
InitializeUserCulture(new HttpContextWrapper(HttpContext.Current).GetSessionID());
}
}
private void BeginRequest(object sender, EventArgs e)
{
InitializeCustomer((HttpApplication)sender);
}
private static void InitializeInstantie(HttpApplication application)
{
HttpContextBase contextBase = new HttpContextWrapper(application.Context);
RouteData routeData = RouteTable.Routes.GetRouteData(contextBase);
if (routeData != null)
{
var customer = routeData.Values["customer"];
if (customer != null)
{
Initializer.InitializeCustomer(customer.ToString());
}
}
}
}

Custom route handlers in ASP.Net WebAPI

I'm able to successfully register a custom route handler (deriving from IRouteHandler) inside global.asax.cs for a Web API route ala:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{client}/api/1.0/{controller}/{action}/{id}",
defaults: new{id = UrlParameter.Optional}
).RouteHandler = new SingleActionAPIRouteHandler();
However I can't seem to find a way to do this when trying to setup an in memory host for the API for integration testing, when I call HttpConfiguration.Routes.MapRoute I'm not able to set a handler on the returned IHttpRoute.
Should I be doing this differently (for instance by using a custom HttpControllerSelector)? I'd obviously like to do it the same way in both cases.
Thanks,
Matt
EDIT:
So what I ended up doing was basically following the advice from below, but simply overriding the HttpControllerDispatcher class as follows:
public class CustomHttpControllerDispatcher : HttpControllerDispatcher
{
public CustomHttpControllerDispatcher(HttpConfiguration configuration) : base(configuration)
{
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// My stuff here
return base.SendAsync(request, cancellationToken);
}
}
You are very right. Self host returns IHttpRoute and takes HttpMessageHandler as a parameter. There seems no inbuilt way to specificity a route handler.
Update: To be a bit clearer
You should almost certainly have a go at using a HttpControllerSelector and implementing a custom one... An example being. http://netmvc.blogspot.co.uk/
What follows is a bit of experimentation if the HttpControllerSelector is not sufficent for your requirements for what ever reason...
But, as you can see the MapHttpRoute does have an overload for HttpMessageHandler so you can experiment with this... if the handler is NULL then it defaults to IHttpController but you can implement your own and use this to direct to the correct controller... The MVC framework appears to use [HttpControllerDispatcher] here, so borrowing some code you can place your own controller / route selection code here too - you have access to the route and selector and can swap it in and out yourself.
This CustomHttpControllerDispatcher code is for DEMO only... look for the line
//DO SOMETHING CUSTOM HERE TO PICK YOUR CONTROLLER
And perhaps have a play with that...
Example route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: new CustomHttpControllerDispatcher(config)
);
Example CustomHttpControllerDispatcher:
public class CustomHttpControllerDispatcher : HttpMessageHandler
{
private IHttpControllerSelector _controllerSelector;
private readonly HttpConfiguration _configuration;
public CustomHttpControllerDispatcher(HttpConfiguration configuration)
{
_configuration = configuration;
}
public HttpConfiguration Configuration
{
get { return _configuration; }
}
private IHttpControllerSelector ControllerSelector
{
get
{
if (_controllerSelector == null)
{
_controllerSelector = _configuration.Services.GetHttpControllerSelector();
}
return _controllerSelector;
}
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return SendAsyncInternal(request, cancellationToken);
}
private Task<HttpResponseMessage> SendAsyncInternal(HttpRequestMessage request, CancellationToken cancellationToken)
{
IHttpRouteData routeData = request.GetRouteData();
Contract.Assert(routeData != null);
//DO SOMETHING CUSTOM HERE TO PICK YOUR CONTROLLER
HttpControllerDescriptor httpControllerDescriptor = ControllerSelector.SelectController(request);
IHttpController httpController = httpControllerDescriptor.CreateController(request);
// Create context
HttpControllerContext controllerContext = new HttpControllerContext(_configuration, routeData, request);
controllerContext.Controller = httpController;
controllerContext.ControllerDescriptor = httpControllerDescriptor;
return httpController.ExecuteAsync(controllerContext, cancellationToken);
}
}

Disable Session state per-request in ASP.Net MVC

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)
Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.
I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)
So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!
If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller
[SessionState(SessionStateBehavior.ReadOnly)]
See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.
Thanks to https://stackoverflow.com/a/4235006/372926
Rather than implementing an action filter for this, why don't you implement a RouteHandler?
Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.
If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.
This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.
I also came across the same problem and after doing R&D this link worked for me
Reference:
https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
Create custom Attribute
Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
Register it in global.aspx
1> Create custom Attribute
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior SessionBehavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
{
SessionBehavior = sessioBehavior;
}
}
2. Override
public class SessionControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return SessionStateBehavior.Default;
var actionName = requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
// [Line1]
var cntMethods = controllerType.GetMethods()
.Where(m =>
m.Name == actionName &&
( ( typeOfRequest == typeof(HttpPostAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
)
||
( typeOfRequest == typeof(HttpGetAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
)
)
);
MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
if (actionMethodInfo != null)
{
var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (sessionStateAttr != null)
{
return sessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
3. Register class in Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// --- other code ---
ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
}
}
Try serving the images from another domain. So something like images.mysite.com.
This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.
Have you considered setting up a HttpHandler to serve up your images?
SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.
Idea is to tell the asp.net that specific request wont use session object.
So, Create a custom route handler for specific requests
public class CustomRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
return new MvcHandler(requestContext);
}
}
SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.
After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax
routes.Add("Default", new Route(
"{controller}/{action}",
new RouteValueDictionary(new { controller = "Home", action = "Index"}),
new CustomRouteHandler()
));
Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.
Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.
1.Set web.config/system.web/sessionState:mode="Off".
2.create DictionaryTempDataProvider class.
public class DictionaryTempDataProvider : ITempDataProvider
{
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
return new Dictionary<string, object>();
}
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
}
}
3.Create DictionaryTempDataControllerFactory
public class DictionaryTempDataControllerFactory : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName) as Controller;
if (controller!=null)
controller.TempDataProvider = new DictionaryTempDataProvider();
return controller;
}
}
4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
new DictionaryTempDataControllerFactory()
);
}
On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.
Is it possible that your ASP.NET app is serving the files instead of IIS?
Create new Controller
Decorate controler with [SessionState(SessionStateBehavior.Disabled)]
Refactor code you want seesion stated disabled for to that controller
I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += OnPostMapRequestHandler;
}
private void OnPostMapRequestHandler(object sender, EventArgs e)
{
var context = (sender as HttpApplication).Context;
DisableSessionForSomeRequests(context);
}
private void DisableSessionForSomeRequests(HttpContext context)
{
if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
{
context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{ }
}

Resources