tool/script to visit all pages in an ASP.NET project? - asp.net

Does anybody know of a tool, script, package, whatever that I can use to visit all pages in an ASP.NET Webforms web application project? (we aren't using any MVC functionality)
Preferably, I would like to be able to generate a list of URLs to hit, edit the list so I can add some query string params, hit all the pages in the list, and collect HTTP response codes: (200, 404, 500, 301, whatever).

Design time
Instead of using string literals for URLs in your application, define Url() methods in each page class like this:
public static string Url() { get { return "~/this_page.aspx"; } }
public static string Url(int ID) { get { return "~/this_page.aspx?id=" + ID; } }
Or list all URLs in a static class
public static class URL {
public static string Login() { get { return "~/login.aspx"; } }
public static string DisplayRecord(int recordID)
{ get { return "~/display.aspx?id=" + recordID; } }
Runtime
Use a web testing framework to crawl all links and edit the result. I blogged about one possible solution using Selenium.

I made a WinForms application that gets the pages that can be accessed from the .csproject and can open them by clicking a button.
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using System.Xml.XPath;
namespace OpenAllPages
{
public partial class Form1 : Form
{
public static IList<string> Pages;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string xmlstring = ReadXml("TaskManager.csproj");
Pages = ParseAllPages(xmlstring);
pagesListBox.DataSource = Pages;
}
private string ReadXml(string location)
{
try
{
var myFile = new StreamReader(location);
string myString = myFile.ReadToEnd();
myFile.Close();
return myString;
}
catch (Exception e)
{
MessageBox.Show(String.Format("An error occurred: '{0}'", e.Message));
}
return null;
}
private IList<string> ParseAllPages(string xmlstring)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlstring);
XPathNavigator nav = xmlDoc.DocumentElement.CreateNavigator();
XmlNamespaceManager manager = new XmlNamespaceManager(nav.NameTable);
manager.AddNamespace("x", "http://schemas.microsoft.com/developer/msbuild/2003");
var elements = nav.Select("x:ItemGroup/x:Content", manager);
var pageList = new List<string>();
while (elements.MoveNext())
{
var page = elements.Current.GetAttribute("Include", "");
if (page.EndsWith(".aspx"))
pageList.Add(page);
}
return pageList as IList<string>;
}
private string AddPagePrefix(string page)
{
return "http://localhost:8080/" + page;
}
private void openAllButton_Click(object sender, EventArgs e)
{
foreach (string page in Pages)
System.Diagnostics.Process.Start("chrome.exe", AddPagePrefix(page));
}
}
}
Here is a link to the code
You need to place the project file which contains the pages you want to open in the OpenAllPages project and set it's Copy to Output property to "Copy if newer".
I Form1_Load change TaskManager.csproj to the name of your project file.
And in:
System.Diagnostics.Process.Start("chrome.exe", AddPagePrefix(page));
rename parameter to the executable of the browser you are using.

Related

execute class library dll from url browser

i'm building a class library project using c# to scale/resize images,the project only has one class with only one public static function with 2 parameters now the code is working fine and perfect so its not the issue here.
now, how can i execute this function directly from url?
ex:my project called: myDLL.dll
how to do this:
img src="/myDLL.dll?image=/images/pic1.png&width=200"
so this execute my function and pass width and image as parameters
i know how to add iis handler to execute DLL from browser..but i dont know how to make this :/myDLL.dll?image=/images/pic1.png&width=200 run my function
plz help
Yes, you can.
HTTP Handler
public class Class1 : IHttpHandler
{
public bool IsReusable => false;
public void ProcessRequest(HttpContext context)
{
Assembly ass = Assembly.LoadFile(context.Request.PhysicalPath);
Type[] assemblyTypes = ass.GetTypes();
for (int j = 0; j < assemblyTypes.Length; j++)
{
if (assemblyTypes[j].Name == "WebDLL")
{
object o = Activator.CreateInstance(assemblyTypes[j]);
MethodInfo mi = assemblyTypes[j].GetMethod("ProcessRequest");
mi.Invoke(o, new object[] { context });
}
}
}
}
DLL
public class WebDLL
{
public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello World!");
}
}
It will output Hello World!

Event and error logging in Asp.net MVC 5 project

I am looking at implementing a logging mechanism in a site of mine, I wish to do basic user action logging. I don't want to log every single button they click on, but I do want to log actions they do which makes changes.
Are there any libraries or articles / tutorials out there which can help me implement a good and efficient logging mechanism for my asp.net site. Im not sure if there are any changes in MVC5 that might come in use for logging as I know user Authentication and Authorization has changed a fair amount from 4 to 5.
I'm sure that there is a dynamic library out there that will work in many different situations.
Nice to haves:
Async capability
Scalable
Simple to use
I'm thinking along the lines of creating a custom filter or attribute that then logs the suers action, but that's just my Idea, Im here to ask what the standard / industry way to do it is.
There isn't an industry standard.
I've used filters or I've overridden the "onActionExecuting" method on the base controller class to record controller / action events.
EDIT ::
Trying to be more helpful but this is really vague.
If you're worried about errors and things like that use elmah.
For other logging use Nlog or Log4Net.
If you're trying to do extra logging like auditing or something like that you can use any combination of those, or something custom. In my site I created a table that stores every click on the site by creating an object sort of like this :
public class audit
{
public int ID { get; set; }
public DateTime AuditDate { get; set; }
public string ControllerName { get; set; }
public string ActionName { get; set; }
public Dictionary<string, object> values
}
In my base constructor, I overrode the OnActionExecuting event :
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
checkForLogging(ctx);
//do not omit this line
base.OnActionExecuting(ctx);
}
Lets say I want to log all Get Requests using my new audit object
private void checkForLogging(ActionExecutingContext ctx)
{
//we leave logging of posts up to the actual methods because they're more complex...
if (ctx.HttpContext.Request.RequestType == "GET")
{
logging(ctx.ActionDescriptor.ActionName, ctx.ActionDescriptor.ControllerDescriptor.ControllerName, ctx.ActionParameters);
}
}
Thats all the info I need to fill my logging object with the action name, the controller name and all the params passed into the method. You can either save this to a db, or a logfile or whatever you want really.
The point is just its a pretty big thing. This is just one way to do it and it may or may not help you. Maybe define a bit more what exactly you want to log and when you want to do it?
You can create a custom attribute and decorate methods with it and then check if that attribute is present when the OnActionExecuting method fires. You can then get that filter if present and read from it and use that to drive your logging if you want...
Maybe this example will help.
My focus on logging is in the CREATE, EDIT, DELETE actions.
I am using MVC 5 Code-first EF 6.1 (VS 2013) ,
and for this example I are referring to the Create action for an entity called "WorkFlow"
I actually view these logs from SSRS, but you could add a controller and Views for WriteUsageLog and view them from the MVC application
MODEL: Create a MODEL Entity called "WriteUsageLog" which will be where the log records are kept
CONTROLLER: Extract, or refactor, the HttpPost overload of the "Create" action from the WorkFlowController into a Partial Class called "WorkFlowController" (These partials are to avoid being deleted and rebuilt when I use the wizard to create Controllers)
Other Classes in the CONTROLLER folder: Then there are some helper functions that are required in a class called "General_Object_Extensions" and "General_ActiveDirectory_Extensions" (NOTE: these are not really 'extensions')
Add the following line to the DBContext:
public DbSet WriteUsageLogs { get; set; }
The advantage of this example is:
I am recording the following for the record:
User Name from Active Directory
The DateTime that the log record is being created
The computer name
And a Note that consists of the values for all the entity properties
I am recording the log in a table from which I can access it either using an MVC controller, or preferably from SQL Server Report Server. Where I can monitor all my MVC applications
/Models/WriteUsageLog.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MileageReimbursement.Models
{
public class WriteUsageLog
{
public WriteUsageLog()
{
this.DateTimeCreated = DateTime.Now; // auto-populates DateTimeCreated field
}
[Key]
public int WriteUsageLogID { get; set; }
[Column(TypeName = "nvarchar(max)")]
public string Note { get; set; }
public string UserLogIn { get; set; }
public string ComputerName { get; set; }
public DateTime DateTimeCreated { get; private set; } //private set to for auto-populates DateTimeCreated field
}
}
/Controllers/ControllerPartials.cs
using System.Linq;
using System.Web.Mvc;
using MileageReimbursement.Models;
//These partials are to avoid be deleted and rebuilt when I use the wizard to create Controllers
namespace MileageReimbursement.Controllers
{
public partial class WorkFlowController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "whatever")] WorkFlow workFlow)
{
...
if (ModelState.IsValid)
{
db.WorkFlows.Add(workFlow);
db.SaveChanges();
//===================================================================================================================
string sX = workFlow.GetStringWith_RecordProperties();
//===================================================================================================================
var logRecord = new WriteUsageLog();
logRecord.Note = "New WorkFlow Record Added - " + sX;
logRecord.UserLogIn = General_ActiveDirectory_Extensions.fn_sUser();
string IP = Request.UserHostName;
logRecord.ComputerName = General_functions.fn_ComputerName(IP);
db.WriteUsageLogs.Add(logRecord);
db.SaveChanges();
//===================================================================================================================
return RedirectToAction("Index");
}
else // OR the user is directed back to the validation error messages and given an opportunity to correct them
{
...
return View(workFlow); //This sends the user back to the CREATE view to deal with their errors
}
}
}
}
/Controllers/ControllerExtensions.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace MileageReimbursement.Controllers
{
public static class General_ActiveDirectory_Extensions
{
public static string fn_sUser()
{
char cX = '\\';
string sUser = General_functions.fn_ReturnPortionOfStringAfterLastOccuranceOfACharacter(HttpContext.Current.User.Identity.Name, cX);
return sUser; //returns just the short logon name Example for 'accessiicarewnc\ggarson', it returns 'ggarson'
}
} //General_ActiveDirectory_Extensions
public static class General_Object_Extensions
{
public static string GetStringWith_RecordProperties(this object Record)
{
string sX = null;
Dictionary<string, object> _record = GetDictionary_WithPropertiesForOneRecord(Record);
int iPropertyCounter = 0;
foreach (var KeyValuePair in _record)
{
iPropertyCounter += 1;
object thePropertyValue = _record[KeyValuePair.Key];
if (thePropertyValue != null)
{
sX = sX + iPropertyCounter + ") Property: {" + KeyValuePair.Key + "} = [" + thePropertyValue + "] \r\n";
}
else
{
sX = sX + iPropertyCounter + ") Property: {" + KeyValuePair.Key + "} = [{NULL}] \r\n";
}
}
return sX;
}
public static Dictionary<string, object> GetDictionary_WithPropertiesForOneRecord(object atype)
{
if (atype == null) return new Dictionary<string, object>();
Type t = atype.GetType();
PropertyInfo[] props = t.GetProperties();
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (PropertyInfo prp in props)
{
object value = prp.GetValue(atype, new object[] { });
dict.Add(prp.Name, value);
}
return dict;
}
} //General_Object_Extensions
public static class General_functions
{
public static string fn_ComputerName(string IP)
{
//USAGE
//From: http://stackoverflow.com/questions/1444592/determine-clients-computer-name
//string IP = Request.UserHostName;
//string compName = CompNameHelper.DetermineCompName(IP);
IPAddress myIP = IPAddress.Parse(IP);
IPHostEntry GetIPHost = Dns.GetHostEntry(myIP);
List<string> compName = GetIPHost.HostName.ToString().Split('.').ToList();
return compName.First();
}
static public string fn_ReturnPortionOfStringAfterLastOccuranceOfACharacter(string strInput, char cBreakCharacter)
{
// NOTE: for path backslash "/", set cBreakCharacter = '\\'
string strX = null;
//1] how long is the string
int iStrLenth = strInput.Length;
//2] How far from the end does the last occurance of the character occur
int iLenthFromTheLeftOfTheLastOccurance = strInput.LastIndexOf(cBreakCharacter);
int iLenthFromTheRightToUse = 0;
iLenthFromTheRightToUse = iStrLenth - iLenthFromTheLeftOfTheLastOccurance;
//3] Get the Portion of the string, that occurs after the last occurance of the character
strX = fn_ReturnLastXLettersOfString(iLenthFromTheRightToUse, strInput);
return strX;
}
static private string fn_ReturnLastXLettersOfString(int iNoOfCharToReturn, string strIn)
{
int iLenth = 0;
string strX = null;
int iNoOfCharacters = iNoOfCharToReturn;
iLenth = strIn.Length;
if (iLenth >= iNoOfCharacters)
{
strX = strIn.Substring(iLenth - iNoOfCharacters + 1);
}
else
{
strX = strIn;
}
return strX;
}
} //General_functions
}
I would agree that Log4Net and NLog seem to be the two most commonly used products on the different projects I have been a member.
If you are looking for a great tool that you can use for logging, error handling and anything else where AOP would be beneficial I would highly recommend PostSharp (http://www.postsharp.net/). You set your logging/error handling up centrally and then just decorate methods. It is a well documented and supported product. They have a community license, which is free - and it is free for individuals. They also have professional and ultimate versions of the products, which would make more sense if you're using it as a team.
I don't work at PostSharp :-) I've just used it in the past and really like it.

asp.net MVC 3/4 equivalent to a response.filter

I am in a need to intercept all of the html that will be sent to the browser and replace some tags that are there. this will need to be done globally and for every view. what is the best way to do this in ASP.NET MVC 3 or 4 using C#? In past I have done this in ASP.net Webforms using the 'response.filter' in the Global.asax (vb)
Private Sub Global_PreRequestHandlerExecute(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRequestHandlerExecute
Response.Filter = New ReplaceTags(Response.Filter)
End Sub
this calls a class I created that inherits from the system.io.stream and that walked through the html to replace all the tags.
I have no idea as to how to do this in ASP.NET MVC 4 using C#. As you might have noticed I am a completely newbee in the MVC world.
You could still use a response filter in ASP.NET MVC:
public class ReplaceTagsFilter : MemoryStream
{
private readonly Stream _response;
public ReplaceTagsFilter(Stream response)
{
_response = response;
}
public override void Write(byte[] buffer, int offset, int count)
{
var html = Encoding.UTF8.GetString(buffer);
html = ReplaceTags(html);
buffer = Encoding.UTF8.GetBytes(html);
_response.Write(buffer, offset, buffer.Length);
}
private string ReplaceTags(string html)
{
// TODO: go ahead and implement the filtering logic
throw new NotImplementedException();
}
}
and then write a custom action filter which will register the response filter:
public class ReplaceTagsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var response = filterContext.HttpContext.Response;
response.Filter = new ReplaceTagsFilter(response.Filter);
}
}
and now all that's left is decorate the controllers/actions that you want to be applied this filter:
[ReplaceTags]
public ActionResult Index()
{
return View();
}
or register it as a global action filter in Global.asax if you want to apply to all actions.
The answer is correct but. After using it for a while I came across a case when the response is split in many parts so that html is incorrect
Part 1:
<html>.....<labe
Part 2:
l/>...</html>
Also partial renders may make unexpected cases. Their html is out of the main stream too.
So my solution is to do it in the Flush method after all streaming is done.
/// <summary>
/// Insert messages and script to display on client when a partial view is returned
/// </summary>
private class ResponseFilter : MemoryStream
{
private readonly Stream _response;
private readonly IList<object> _detachMessages;
public override void Flush()
{
// add messages and remove
// filter is called for a number of methods on one page (BeginForm, RenderPartial...)
// so that we don't need to add it more than once
var html = MessageAndScript(_detachMessages);
var buffer = Encoding.UTF8.GetBytes(html);
_detachMessages.Clear();
_response.Write(buffer, 0, buffer.Length);
base.Flush();
}
public ResponseFilter(Stream response, IList<object> detachMessages)
{
_response = response;
_detachMessages = detachMessages;
}
public override void Write(byte[] buffer, int offset, int count)
{
_response.Write(buffer, offset, buffer.Length);
}
private static string MessageAndScript(IList<object> detachMessages)
{
if (detachMessages.Count == 0)
return null;
var javascript = CustomJavaScriptSerializer.Instance.Serialize(detachMessages);
return "$(function(){var messages = " + javascript + #";
// display messages
base.ajaxHelper.displayMessages(messages);
})";
}
}

Partial caching of custom WebControls

I need to cache the generated content of custom WebControls. Since build up of control collection hierarchy is very expensive, simple caching of database results is not sufficient. Caching the whole page is not feasible, because there are other dynamic parts inside the page.
My Question: Is there a best practice approach for this problem? I found a lot of solutions caching whole pages or static UserControls, but nothing appropriate for me. I ended up with my own solution, but im quite doubtful if this is a feasible approach.
A custom WebControl which should be cached could look like this:
public class ReportControl : WebControl
{
public string ReportViewModel { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Fake expensive control hierarchy build up
System.Threading.Thread.Sleep(10000);
this.Controls.Add(new LiteralControl(ReportViewModel));
}
}
The aspx page which includes the content control(s) could look as follows:
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Fake authenticated UserID
int userID = 1;
// Parse ReportID
int reportID = int.Parse(Request.QueryString["ReportID"]);
// Validate if current user is allowed to view report
if (!UserCanAccessReport(userID, reportID))
{
form1.Controls.Add(new LiteralControl("You're not allowed to view this report."));
return;
}
// Get ReportContent from Repository
string reportContent = GetReport(reportID);
// This controls needs to be cached
form1.Controls.Add(new ReportControl() { ReportViewModel = reportContent });
}
private bool UserCanAccessReport(int userID, int reportID)
{
return true;
}
protected string GetReport(int reportID)
{
return "This is Report #" + reportID;
}
}
I ended up writing two wrapper controls, one for capturing generated html and a second one for caching the content - Quite a lot of code for simple caching functionality (see below).
The wrapper control for capturing the output overwrites the function Render and looks like this:
public class CaptureOutputControlWrapper : Control
{
public event EventHandler OutputGenerated = (sender, e) => { };
public string CapturedOutput { get; set; }
public Control ControlToWrap { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Controls.Add(ControlToWrap);
}
protected override void Render(HtmlTextWriter writer)
{
StringWriter stringWriter = new StringWriter();
HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter);
base.RenderChildren(htmlTextWriter);
CapturedOutput = stringWriter.ToString();
OutputGenerated(this, EventArgs.Empty);
writer.Write(CapturedOutput);
}
}
The wrapper control to cache this generated output looks as follows:
public class CachingControlWrapper : WebControl
{
public CreateControlDelegate CreateControl;
public string CachingKey { get; set; }
public delegate Control CreateControlDelegate();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string content = HttpRuntime.Cache.Get(CachingKey) as string;
if (content != null)
{
// Content is cached, display
this.Controls.Add(new LiteralControl(content));
}
else
{
// Content is not cached, create specified content control and store output in cache
CaptureOutputControlWrapper wrapper = new CaptureOutputControlWrapper();
wrapper.ControlToWrap = CreateControl();
wrapper.OutputGenerated += new EventHandler(WrapperOutputGenerated);
this.Controls.Add(wrapper);
}
}
protected void WrapperOutputGenerated(object sender, EventArgs e)
{
CaptureOutputControlWrapper wrapper = (CaptureOutputControlWrapper)sender;
HttpRuntime.Cache.Insert(CachingKey, wrapper.CapturedOutput);
}
}
In my aspx page i replaced
// This controls needs to be cached
form1.Controls.Add(new ReportControl() { ReportViewModel = reportContent });
with
CachingControlWrapper cachingControlWrapper = new CachingControlWrapper();
// CachingKey - Each Report must be cached independently
cachingControlWrapper.CachingKey = "ReportControl_" + reportID;
// Create Control Delegate - Control to cache, generated only if control does not exist in cache
cachingControlWrapper.CreateControl = () => { return new ReportControl() { ReportViewModel = reportContent }; };
form1.Controls.Add(cachingControlWrapper);
Seems like a good idea, maybe you should pay attention to :
the ClientIdMode of the child controls of your custom control to prevent conflicts if these controls are to be displayed in another context
the LiteralMode of your Literal : it should be PassThrough
the expiration mode of your cached item (absoluteExpiration/slidingExpiration)
disable ViewState of your CustomControl
Recently, I tend to have another approach : my wrapper controls only holds some javascript that performs an AJAX GET request on a page containing only my custom control.
Caching is performed client side through http headers and serverside through OutputCache directive (unless HTTPS, content has to be public though)

ASP.NET + NUnit : Good unit testing strategy for HttpModule using .NET 4

I have the following HttpModule that I wanted to unit test. Problem is I am not allowed to change the access modifiers/static as they need to be as it is. I was wondering what would be the best method to test the following module. I am still pretty new in testing stuff and mainly looking for tips on testing strategy and in general testing HttpModules. Just for clarification, I am just trying to grab each requested URL(only .aspx pages) and checking if the requested url has permission (for specific users in our Intranet). So far it feels like I can't really test this module(from productive perspective).
public class PageAccessPermissionCheckerModule : IHttpModule
{
[Inject]
public IIntranetSitemapProvider SitemapProvider { get; set; }
[Inject]
public IIntranetSitemapPermissionProvider PermissionProvider { get; set; }
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += ValidatePage;
}
private void EnsureInjected()
{
if (PermissionProvider == null)
KernelContainer.Inject(this);
}
private void ValidatePage(object sender, EventArgs e)
{
EnsureInjected();
var context = HttpContext.Current ?? ((HttpApplication)sender).Context;
var pageExtension = VirtualPathUtility.GetExtension(context.Request.Url.AbsolutePath);
if (context.Session == null || pageExtension != ".aspx") return;
if (!UserHasPermission(context))
{
KernelContainer.Get<UrlProvider>().RedirectToPageDenied("Access denied: " + context.Request.Url);
}
}
private bool UserHasPermission(HttpContext context)
{
var permissionCode = FindPermissionCode(SitemapProvider.GetNodes(), context.Request.Url.PathAndQuery);
return PermissionProvider.UserHasPermission(permissionCode);
}
private static string FindPermissionCode(IEnumerable<SitemapNode> nodes, string requestedUrl)
{
var matchingNode = nodes.FirstOrDefault(x => ComparePaths(x.SiteURL, requestedUrl));
if (matchingNode != null)
return matchingNode.PermissionCode;
foreach(var node in nodes)
{
var code = FindPermissionCode(node.ChildNodes, requestedUrl);
if (!string.IsNullOrEmpty(code))
return code;
}
return null;
}
public void Dispose() { }
}
For other people still looking there is this post which explains a way to do it
Original page was deleted, you can get to the article here:
https://web.archive.org/web/20151219105430/http://weblogs.asp.net/rashid/unit-testable-httpmodule-and-httphandler
Testing HttpHandlers can be tricky. I would recommend you create a second library and place the functionality you want to test there. This would also get you a better separation of concerns.

Resources