Why is accessing session state and HttpContext in WebAPI considered bad design? - asp.net

I have several .asmx web services that I want to upgrade to WebAPI. These web services look somewhat like this:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class TheWebService : System.Web.Services.WebService {
[WebMethod(EnableSession = true)]
public string SomeMethod(string SomeInput)
{
MySessionModel TheSession = HttpContext.Current.Session["UserSession"] as MySessionModel;
return SomeClass.SomeMethod(SomeInput, TheSession);
}
}
Basically, I have a single-page application. I'm using Forms Auth to login and redirect users to their "profile" and then, from this page, the app uses web services to communicate with the server. The web services only return raw strings so I don't need serialization at teh web service level. For the moment, the app is hosted in IIS and soon I'll be deploying it into azure.
I've looked around on the web, and several posts suggest that using session state and HttpContext is bad design. Why is using HttpCurrent and session state a bad idea in this case?

There is nothing innately wrong with using ASP.NET Session, as long as you don't use it as a catch-all basket for any old data. Shopping carts, for example, do not belong in Session: they belong in a Shopping Cart persistence component.
Also, and I suspect the reason for the Azure tag on this question, if you are running in a load-balanced environment such as an Azure Cloud Service, you need to use an external session provider such as a SQL Database or a shared cache. Using the in-process session provider (the default) will cause very odd, often unreproducable bugs as users are switched between different servers with different copies of the session.
As for HttpContext.Current, well, for Web API, things like Inversion of Control, Dependency Injection, and simple testability are important. A clean, testable Web API version of that service might look something like this:
public class TheWebService : ApiController {
private readonly IUserSession _userSession;
public TheWebService(IUserSession userSession)
{
_userSession = userSession;
}
public string SomeMethod(string SomeInput)
{
MySessionModel TheSession = _userSession.Get();
return SomeClass.SomeMethod(SomeInput, TheSession);
}
}
public interface IUserSession
{
MySessionModel Get();
}
You could still use HttpContext.Current.Session["UserSession"] in a class like this:
public class CurrentContextUserSession : IUserSession
{
public MySessionModel Get()
{
return HttpContext.Current.Session["UserSession"] as MySessionModel;
}
}
You would then use an IoC container such as Unity or Ninject to set CurrentContextUserSession as the implementation of IUserSession for Web API to use when constructing instances of TheWebService. But when you were writing your tests, you could use a mock or stub implementation of IUserSession that had no dependency on HttpContext.Current.

In your specific example, you are using the Session only inside the WebMethod, which is fine as it is already coupled to ASP.NET but many people tend to use this at other layers of their application which is a really bad design.
Common problems of using HttpContext.Current in those layers are:
the code cannot be easily unit tested in isolation
the code is tightly coupled to an ASP.NET context
This being said, having stateful services that depend on the session is bad design. In the example you have shown, that's an ASMX WebService which is depending on the ASP.NET Session state meaning that the client should be passing cookies around in order to invoke this service. Also stateful services are harder to scale.

Related

How to set session use by asp.net web api with jquery (not use mvc)

I want to know how to set session using ASP.Net Web API (not ASP.net MVC) and jQuery.
Any idea?
Rule of thumb is not to use sessions with Web API due to the stateless nature of the web services. but you can access session modifying Global.ascx
file in the project.
protected void Application_PostAuthorizeRequest()
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
to set the same session between multiple servers you can use single SQL instance
as the session state.
then you can use that session inside your controller code
[HttpPost]
public string GetSetSession(string stringValue)
{
Session["session_key"]=stringValue;
return Session["session_key"].ToString();
}

How to get NTID in a service layer from IIS in .Net Core 2.0

Some background, I built my first .Net Core 2.0 API app that is hosted in a company intranet and used only by internal employees. The app needs to know who the user is on all pages, to work properly, but we don't want to add login/logout/authentication/sessions since the information doesn't need to be secured, it's only to personalize the user's data. I have enabled Windows Authentication successfully and I'm seeing the username (DOMAIN/USERNAME) displayed to the screen when I use the following in a controller:
User.Identity.Name
However, I wanted to get the same username (NTID) in my UserService instead, so that anytime the username is needed, any of the Services can call UserService to get the username.
I have tried all of the following in my UserService, but none of them provide the NTID from IIS:
...
return WindowsIdentity.GetCurrent().Name;
...
return System.Threading.Thread.CurrentPrincipal.ToString();
...
return Environment.UserName;
....
Since this is a REST API, I won't be using Views (I'm aware you can get the username in the View).
Is there an easy approach to get the username outside of the controller? I have found multiple examples online but they are all for < .Net Core 2.0.
In core, HttpContext is injected now, instead of being a thread static. That means the old style of using a static accessor like what you're trying won't work.
To access the HttpContext in something it is not automatically injected into (like a controller), you need to inject IHttpContextAccessor. For example:
public class UserService
{
protected readonly IHttpContextAccessor httpContextAccessor;
public UserService(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
...
}
Then, as long as you register your service via the ASP.NET Core DI container, an instance will be injected automatically into your service. You can then simply do:
var username = httpContextAccessor.HttpContext.User.Identity.Name;

Cannot share ASP.NET session with wcf service

So, I have on same machine asp.net app and wcf service. And I'm trying to share asp.net session between them.
I have configured Sql server to store my session data.
I have ASPState Db and two tables in tempdb: ASPStateTempSessions, ASPStateTempApplications
And both have rows with data.
In asp.net app and wcf service configs I have added :
<sessionState mode="SQLServer" sqlConnectionString="Data Source=localhost;integrated security=True;Application Name=app" />
In Asp.net app I set user like this
HttpContext.Current.Session["User"] = userName;
In logs I saw an entry that user was actually setted. (and checked via JS)
In wcf service:
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IDbWebService
{
[OperationContract]
[WebInvoke]
Data GetBlahBlah(string env);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public partial class WebService : IDbWebService
{
public Data GetBlahBlah(string env)
{
//trying to get data here, nothing, just null
_log.InfoFormat("USER: {0}", HttpContext.Current.Session["User"]);
}
}
But I cannot get Session["User"].
What I'm doing wrong, maybe I have missed something obvious ?
Thanks in advance!
See this post:
Losing Session State with ASP.NET/SQL Server
Make sure your application ID's are the same in IIS manager and that you have the same machine key set in both configs. Both are used in the generation and encryption of session state, so if they are different you will not be able to share session entries.
On a separate note, relying on Session State to pass information between an application and a web service would seem to violate layered responsibility patterns. Your web service is now reliant on the client to provide data through this backdoor means that is not represented anywhere in the service contract. Another client consuming the service would have to provide data in the same fashion in order for the service to function correctly. Is there a reason why the data can't be supplied in the method signature?

How to share asp.net Session into WCF service

Im using asp.net website with WCF service, having wsHttpBinding,Aspnet compatibility enabled, specified as Sessionmode -allowed, service behavior- isinitiated and client session cookie enabled. Its looking like Asp.Net session object and WCF Session( HTTPContext.Current.Session) work independently. How can I share Asp.net Session value to WCF Session and vise versa.
Instead of making your web service dependent on the hosting environment I would suggest you adding the needed parameter to the operation contract so that it is passed by the consumer which in your case is an ASP.NET application that will fetch it from the session.
in your service interface:
[ServiceContract]
public interface IService
{
[OperationContract]
string GetSessionValue();
[OperationContract]
void SetSessionValue(string value);
}
in your svc:
public class Service1 : IService
{
public string GetSessionValue()
{
return "your value";
}
public void SetSessionValue(string value)
{
// your code here
}
}
and finally consume that method from web code behind.
I searched a lot but could not find a way. Only option I found is use cookie enabled service in same virtual directory. Better to keep it as RESTful service
Look at "Managing shared cookies in WCF"

How to provide a Session/Host object for use in both a windows and web application?

I have a web application that makes heavy use of the Session state to store information about the current user, their personal settings, record their session history and so on.
I have found myself retrieving this session information in my business layer, like so:
((UserSession)HttpContext.Current.Session["UserSession"]).User.Info
This poses a problem - at some point in the future my application will have a Windows client which obviously cannot reference the web Session state. So I need a host or customized session class that I can reference in my business layer that is agnostic of whether the application is running on the web or desktop. Something like:
IHost.User.Info
Behind the scenes, the web implementation will obviously utilize the Session state to store information, but I need to hide this away from my business layer. Has anyone solved this problem or got any practival advice on how best to approach this?
Help appreciated.
Assuming that the business layer is a separate DLL, I would never add a reference to System.Web and in consequence I would never use the Session object directly. This would lead to a different design of the business layer and of the exposed interfaces to a client (either web or winforms).
That said, as a quick workaround I would suggest to write a wrapper class in your business layer that hides the Session object from your code. Your calls from code will be something like this:
((UserSession) DualContext.Current["UserSession"]).User.Info
and the wrapper implementation will be something like this (not completed and tested):
public class DualContext
{
private Dictionary<string, object> winFormsSession = new Dictionary<string, object>();
private static readonly DualContext instance = new DualContext();
public static DualContext Current
{
get { return instance; }
}
public object this[string key]
{
get
{
if (HttpContext.Current != null)
return HttpContext.Current.Session[key];
else
return winFormsSession[key];
}
set
{
if (HttpContext.Current != null)
HttpContext.Current.Session[key] = value;
else
winFormsSession[key] = value;
}
}
}
It would take some re-architecting, but if you switch from using Session State to User Profiles you could then use Client Application Services to share the information.
http://msdn.microsoft.com/en-us/library/bb384297.aspx
I guess you need to create a webservice or RESTfull service. The service will return an XML file representing your user information. You will be able to invoke the service wither from you windows or web application.

Resources