I have one main mvc application that will call 3 web api applications and will write logs using log.LogInformation() for each method hit and each controller in the mvc project. I created a variable named username and have each of the logs to include that username at the end of the message. I.e. log.LogInformation("You have hit the home page of the mvc project... {#Username}, username)so at the end of every log message I am using {#Username} and passing username. I am wanting to group all the entire logs based on that variable username. Is there a way to group all the entire logs that get recorded to logger, starting from the mvc project to every web api project called? I am using Serilog to write the logs.
Follow up:
So after reading Log scopes and using BeginScope I could create another method in my mvc project in my last controller as so:
public IActionResult GetAllLogs()
{
using (_logger.BeginScope("Gathering all the logs created for {#Username}", HomeController.username))
{
}
}
So leaving it as will be able to gather all the logs from the mvc project that includes the web api projects logs? I wouldn't need to do anything inside of the using() statement?
You can check for Log scopes (Read here). With BeginScope, all the log statement inside the scope will have the property logged as custom property.
For ex:, the Scope is started at SomeAPI API which invokes Method1 -> Method2 -> Method3DoesTheLogging. Now till method Method3DoesTheLogging, no property need to be passed to be logged in log statement. The logging framework will automatically attached the custom property which were defined in BeginScope.
public IActionResult SomeAPI()
{
using (_logger.BeginScope("Gathering all the logs created for {#Username}", HomeController.username))
{
Method1();
}
}
public void Method1()
{
Method2();
}
public void Method2()
{
Method3DoesTheLogging();
}
public void Method3DoesTheLogging()
{
_logger.LogInformation("This log statement will log the username automatically as custom property".);
}
So with Log scope, you don't need to pass the property to nth function level. Since, property is logged as a custom property so you can do grouping, filtering e.t.c,
Related
I am working on a MVC application and I have a requirement of dealing with errors and session timeouts by redirecting the user to different error pages based on few parameters in the query string.
The issue I am facing is that i tried to implement this by saving the required parameters from querystring into a session and then redirecting to error pages. But before every HttpGet and Post action in my controllers I am checking if session is active.
So in case of a situation where session values are lost and not able to read them.
How can I implement this thing in any other way?
You need to check whether the session exists, has the fields you expect and is active. If the session does not exist or does not have a fields you expect, then handle the case when the session does not exist yet/expired. If it is not active, then handle the case when the session is no longer active. If everything is ok, then handle the request normally. If the session expired, then handle it as expired.
to check about session, you can use an ActionFilter like this:
public class SessionActiveFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var activeSession = Session["user"];
if (activeSession == null)
//Here, do a redirect
base.OnActionExecuting(filterContext);
}
}
Also, you can use a third option to save the session, like Redis Cache http://blogs.msdn.com/b/webdev/archive/2014/05/12/announcing-asp-net-session-state-provider-for-redis-preview-release.aspx
I know this is a dead story now. But I post this answer for the new comers. Please see the nice tutorial in codeproject about how to check session values in Action Filters.
In a dynamic web application, the session is crucial to hold the information of current logged in user identity/data. So someone without authentication cannot have access to some Page or any ActionResult, to implement this kind of functionality, we need to check session exists (is not null) in every action which required authentication.So, the general method is as follows:
[HttpGet]
public ActionResult Home()
{
if(Session["ID"] == null)
return RedirectToAction("Login","Home");
}
We have to check the above 2 statements each time and in each ActionResult, but it may cause 2 problems.
Repeat Things: As per the good programming stranded, we don't have to repeat the things. Create a module of common code and access it multiple times/repeatedly
Code missing: We have to write code multiple times so it might happen some time we forget to write code in some method or we missed it.
How To Avoid?
The ASP.NET MVC provides a very great mechanism i.e., Action Filters. An action filter is an attribute. You can apply most action filters to either an individual controller action or an entire controller.
If you want to know more about action filter, please click here.
So we will create a custom Action Filter that handles session expiration and if session is null, redirect to Login Action.
Create a new class in your project and copy the following code:
namespace YourNameSpace
{
public class SessionTimeoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if (HttpContext.Current.Session["ID"] == null)
{
filterContext.Result = new RedirectResult("~/Home/Login");
return;
}
base.OnActionExecuting(filterContext);
}
}
}
Now our Action Filter is created and we are ready to use it. The following code will show you how we can apply attribute to Action or to complete controller.
Apply to Action
[HttpGet]
[SessionTimeout]
public ActionResult MyProfile()
{
return View();
}
Apply to Controller
[SessionTimeout]
public class HomeController : Controller
{
}
Now all actions of Home Controller will check for session when hit with the help of Action Filter. So we have reduced the code and repetitive things. This is the benefits of Action Filters.
There seems to be some weird behavior in ASP.NET Web API (4.0.30506) that I haven't witnessed before.
What I'm seeing is that the same action filter attribute instance is reused over Web API requests. This is especially a problem in case this attribute gets dependencies injected to it, since those dependencies might be specific to the web request. I'm aware that it's better for attributes to be passive, but my assumption was that action filter attributes where not cached.
I searched for any articles, blog posts or Microsoft change logs that described this and the reason behind this, but I couldn't find a single thing. That makes me wonder whether there is something wrong with my configuration that makes this happening. Thing is however, that I'm able to reproduce this issue in a new and empty Visual Studio 2012 Web API project.
What I did was create a new empty project using the Visual Studio 2012 ASP.NET MVC 4 Web Application project with the "Web API" template. It comes with the Web API 4.0.20710.0 NuGet package. After that I added the following attribute:
[DebuggerDisplay("{id}")]
public class TestFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute {
private static readonly List<int> used = new List<int>();
private static int counter;
private readonly int id;
public TestFilterAttribute() {
this.id = Interlocked.Increment(ref counter);
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Just for testing: I would expect this line never to throw, but it does.
if (used.Contains(this.id)) throw new Exception("WAT?");
used.Add(this.id);
base.OnActionExecuting(actionContext);
}
}
And I add this attribute to the ValuesController (part of the default template):
public class ValuesController : ApiController {
// GET api/values
[TestFilterAttribute]
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
// ...
}
Now when I start the project, go to the /api/values in the browser and refresh that page a few times, the "WAT?" exception is thrown.
Is this normal behavior of Web API and if so, what's the reasoning about this? Or Did I miss some memo about this change somewhere? Does this make Web API attributes extra unsuited for doing dependency injection? Or am I'm doing something wrong?
Web API is built on top of MVC, thus it uses a lot of it's functionality.
Attribute instance re-usability is part of the aggressive caching introduced by MVC 3. This means that the same Attribute instance will most likely be used with all the Actions it is applied on. MVC pipeline will do it's best at trying to treat your Attribute class like a Singleton.
Because the same Attribute instance is reused, it's Constructor is not called and id is not incremented. If, for example, you increment id inside OnActionExecuting, all will work well.
You can still do everything you want with your Attribute. You only need to keep in mind that you are not guaranteed to always get a new instance created. The constructor shouldn't contain anything but initial initialization.
public TestFilterAttribute() {
// Instance will be reused thus this will not be called for each Action
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Called on each Action
}
I need to store the current user name for every save request/user in SQL table. It is MVC 4 based application and hosted in IIS server. Also, it is an internal tool and NTLM based authentication.
I have got the username of current user using HttpContext.Current.User.Identity.Name My question is how the global variable reacts in MVC? What would happen for "currentUser" variable when multiple requests comes? Will the currentUser creates new value for every requests? Please help me to understand.
Sample Code:
public class ClearCacheController : ApiController
{
private string currentUser = HttpContext.Current.User.Identity.Name.ToLower();
public void method1()
{
SaveValue1(currentUser);
}
public void method2()
{
SaveValue2(currentUser);
}
public void method3()
{
SaveValue3(currentUser);
}
}
Controllers are instantiated and disposed for each unique request. So, something like User.Identity.Name is not really a global variable. It's an instance variable on the controller. (Well, User is an instance variable on the controller, while Identity is an instance variable on User, etc.). Long and short, it will only hold the value of the user who made the particular request being executed, not any user making any request.
I am Using Web Api 2.1 with Asp.Net Identity 2. I am trying to get the authenticated User on my ApiController's constructor (I am using AutoFac to inject my dependencies), but the User shows as not authenticated when the constructor is called.
I am trying to get the User so I can generate Audit information for any DB write-operations.
A few things I'm doing that can help on the diagnosis:
I am using only app.UseOAuthBearerTokens as authentication with Asp.Net Identity 2. This means that I removed the app.UseCookieAuthentication(new CookieAuthenticationOptions()) that comes enabled by default when you are creating a new Web Api 2.1 project with Asp.Net Identity 2.
Inside WebApiConfig I'm injecting my repository:
builder.RegisterType<ValueRepository>().As<IValueRepository>().InstancePerRequest();
Here's my controller:
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
private IValueRepository valueRepository;
public ValuesController(IValueRepository repo)
{
valueRepository = repo;
// I would need the User information here to pass it to my repository
// something like this:
valueRepository.SetUser(User);
}
protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
// User is not avaliable here either...
}
}
But if I inspect the User object on the constructor, this is what I get:
The authentication is working, if I don't pass my token, it will respond with Unauthorized. If I pass the token and I try to access the user from any of the methods, it is authenticated and populated correctly. It just doesn't show up on the constructor when it is called.
In my WebApiConfig I am using:
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// ... other unrelated injections using AutoFac
}
I noticed that if I remove this line: config.SuppressDefaultHostAuthentication() the User is populated on the constructor.
Is this expected? How can I get the authenticated user on the constructor?
EDIT:
As Rikard suggested I tried to get the user in the Initialize method, but it is still not available, giving me the same thing described in the image.
The problem lies indeed with config.SuppressDefaultHostAuthentication().
This article by Brock Allen nicely explains why that is. The method sets the principal intentionally to null so that default authentication like cookies do not work. Instead, the Web API Authentication filter then takes care of the authentication part.
Removing this configuration when you do not have cookie authentication could be an option.
A neat solution as mentioned here, is to scope the Web API parts of the application, so that you can separate out this configuration to a specific path only:
public void Configuration(IAppBuilder app)
{
var configuration = WebApiConfiguration.HttpConfiguration;
app.Map("/api", inner =>
{
inner.SuppressDefaultHostAuthentication();
// ...
inner.UseWebApi(configuration);
});
}
Don't know if this is still relevant, but I've had exactly the same problems, as you've described above. I've managed to solve it using custom OWIN middleware component.
Some info about my application structure:
Using MVC WebApp and WebAPI in same project (probably not the best option, but I have no time to change it, since deadline is approaching ;))
Using AutoFac as IoC container
Implemented custom ICurrentContext to hold information about currently logged on user (with CookieAuth in MVC and Bearer Token Auth in WebAPI), which is injected where needed (controllers, BAL objects, etc.)
Using EntityFramework 6 for Db access
Converted ASP.NET Identity to use int keys rather than string (http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity)
So on to the code. This is my ICurrentContext interface:
public interface ICurrentContext
{
User CurrentUser { get; set; } // User is my User class which holds some user properties
int? CurrentUserId { get; }
}
and implementation of it:
public class DefaultCurrentContext : ICurrentContext
{
public User CurrentUser { get; set; }
public int? CurrentUserId { get { return User != null ? CurrentUser.Id : (int?)null; } }
}
I've also created an OWIN middleware component:
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
public class WebApiAuthInfoMiddleware : OwinMiddleware
{
public WebApiAuthInfoMiddleware(OwinMiddleware next)
: base(next)
{
}
public override Task Invoke(IOwinContext context)
{
var userId = context.Request.User.Identity.GetUserId<int>();
context.Environment[MyWebApp.Constants.Constant.WebApiCurrentUserId] = userId;
return Next.Invoke(context);
}
}
}
Some information about this component: MyWebApp.Constants.Constant.WebApiCurrentUserId is some string constant (you can use your own) that I've used to avoid typos since its used in more than one place. Basicly what this middleware does, is that it adds current UserId to the OWIN environment dictionary and then Invokes the next action in pipeline.
Then I've created Use* extension statement to include OMC (OWIN Middleware Component) into OWIN pipeline:
using System;
using Owin;
namespace MyWebApp.Web.AppCode.MiddlewareOwin
{
public static class OwinAppBuilderExtensions
{
public static IAppBuilder UseWebApiAuthInfo(this IAppBuilder #this)
{
if (#this == null)
{
throw new ArgumentNullException("app");
}
#this.Use(typeof(WebApiAuthInfoMiddleware));
return #this;
}
}
}
To use this OMC, I've put the Use* statement right after Use* statement for Bearer token inside my Startup.Auth.cs:
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions); // This was here before
// Register AuthInfo to retrieve UserId before executing of Api controllers
app.UseWebApiAuthInfo(); // Use newly created OMC
Now the actual usage of this principle was inside AutoFac's Register method (called on some bootstrap code at the start of web application; in my case this was inside Startup class (Startup.cs), Configuration method) for my ICurrentContext implementation which is:
private static void RegisterCurrentContext(ContainerBuilder builder)
{
// Register current context
builder.Register(c =>
{
// Try to get User's Id first from Identity of HttpContext.Current
var appUserId = HttpContext.Current.User.Identity.GetUserId<int>();
// If appUserId is still zero, try to get it from Owin.Enviroment where WebApiAuthInfo middleware components puts it.
if (appUserId <= 0)
{
object appUserIdObj;
var env = HttpContext.Current.GetOwinContext().Environment;
if (env.TryGetValue(MyWebApp.Constants.Constant.WebApiCurrentUserId, out appUserIdObj))
{
appUserId = (int)appUserIdObj;
}
}
// WORK: Read user from database based on appUserId and create appUser object.
return new DefaultCurrentContext
{
CurrentUser = appUser,
};
}).As<ICurrentContext>().InstancePerLifetimeScope();
}
This method is called where I build AutoFac's container (hence the input parameter of type ContainerBuilder).
This way I got single implementation of CurrentContext, no matter how user was authenticated (via MVC Web Application or Web API). Web API calls in my case were made from some desktop application, but database and most of codebase were the same for MVC App and Web API.
Don't know if its the right way to go, but it has worked for me. Although I am still a little concerned how would this behave thread-wise, since I don't know exactly how using HttpContext.Current inside API calls would behave. I've read somewhere that OWIN Dictionary is used per-request basis, so I think this is safe approach. And I also think that this isn't so neat code, but rather a little nasty hack to read UserId. ;) If there's anything wrong with using this approcah, I'd appreciate any comment regarding it. I've been strugling with this for two weeks now and this is the closest I got of getting UserId in one place (when resolving ICurrentContext from AutoFac through lambda).
NOTE: Wherever there is usage of GetUserId, it can be replaced with original GetUserId (which returns string) implementation. The reason I'm using GetUserId is because I've rewritten ASP.NET to some extent for using ints instead of strings for TKey. I've done this based on following article: http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity
The User property of the controller is not populated until the Initialize method is called which happens after the constructor is invoked, hence thats why the Identity is not yet populated with the authorzied user data.
I realized that removing config.SuppressDefaultHostAuthentication() allowed me to get the Identity in the constructor much earlier. However, I wouldnt suggest doing this if you are using Token Authentication.
Thread.CurrentPrincipical is available throughout the pipeline, you could skip the User registration below:
valueRepository.SetUser(User);
and access
Thread.CurrentPrincipical
In the repository instead, making the repository context aware. Furthermore, you could add a context layer.
If nothing of the above solutions work try this one:
public ActionResult GetFiles()
{
...
string domainID = System.Web.HttpContext.Current.Request.LogonUserIdentity.Name;
...
}
I want to log visits only for some controllers (or routes) as it was possible with classic ASP.NET pages by checking/unchecking the 'log visits' checkbox in IIS.
Does anyone know if this is possible somehow? A solution without a custom logging component would be fantastic! Please share your knowledge, if you know how ;)
Thanks in advance
Create a BaseController which the controllers that you want to record data for inherit from. Then create an ActionFilter which overrides the OnActionExecuted method and apply it to the base controller. Something like this..
public class ActionExecutedFilter : System.Web.Mvc.ActionFilterAttribute
{
UnitOfWork unitOfWork= new UnitOfWork();
public override void OnActionExecuted(ActionExecutedContext filter)
{
Transaction tran = new Transaction();
tran.Controller = filter.ActionDescriptor.ControllerDescriptor.ControllerName;
tran.ActionName = filter.ActionDescriptor.ActionName;
tran.User = HttpContext.Current.User.Identity.Name;
tran.Date = DateTime.Now;
unitOfWork.TransactionRepository.Insert(tran);
unitOfWork.Save();
}
}
This will save to a database table called Transactions information for every time a action method is called on that controller, recording the user, controller and action method name. Obviously I just typed in the UnitOfWork method of saving to the database, you can just plug in whichever method you like. I usually keep these methods in a filters folder, add a using statement then add it to the controller like so;
[ActionExecutedFilter]
public class BaseController : Controller
{
}
Again, just make all the controller you wish to record data from inherit the BaseController.
You could also use something like Log4Net, but I find just doing it this way gets what I need. Whatever you think yourself.
http://dotnetdarren.wordpress.com/2010/07/29/logging-in-mvc-part-4-log4net/
A solution without a custom logging component would be fantastic!
Apart from basic IIS diagnostic logs, I cannot think of any ASP.Net MVC specific Logging features in IIS.
One simple solution what I want to offer is to write a HttpModule. In the HttpModule you can log the requests. Also if you want to have control on what to log and what not to, then based on Routes you can make that happen in HttpModule. Advantage with HttpModule is it is easy to plug in and also easy to remove.
Make HttpModule work only on certain routes
When it comes to logging itself, you can have your own custom logic to database or to log file. But you can opt for ELMAH or Log4Net or Enterprise Logging Application Block