URL Rewriting using ASP.NET 3.5 with C# - c#

I need to rewrite the URL using ASP.NET with code behind as C#. My Application contains the following URL...
http://www.mywebsite.com/Products.aspx?id=1&pg=1
However, I need to rewrite the URL in such a way that the user gets the same contents of the above URL when the user types the following URL...
http://www.mywebsite.com/CategoryName/ProductName/1
Can any of you guys help me with the complete necessary code how to do it?
I mean the web.config, Global.asax, etc...

If you have IIS7, the best option would be to use IIS Url Rewrite Module.

There might be a couple of options I could suggest:
One
You could look at setting up an HttpHandlerFactory.
I have written one myself: http://mvcsnippets.blogspot.co.uk/2012/10/custom-cms-using-httphandlerfactory.html
here is the basic gist:
namespace Web.Helpers {
public class HttpCMSHandlerFactory : IHttpHandlerFactory
{
public IHttpHandler GetHandler(HttpContext context, string requestType, string url,string pathTranslated)
{
string pageName = Path.GetFileNameWithoutExtension(context.Request.PhysicalPath);
//on Server.Transfer the context is kept, so this just overrides the existing value.
if (context.Items.Contains("PageName"))
{
context.Items["PageName"] = pageName; } else { context.Items.Add("PageName", pageName); }
FileInfo fi = new FileInfo(context.Request.MapPath(context.Request.CurrentExecutionFilePath));
//if File is not physical
if (fi.Exists == false)
{
//return page to CMS handler the context containing "PageName" is passed on to this page, which then calls to the database to return the copy.
return PageParser.GetCompiledPageInstance(url, context.Server.MapPath("~/CMSPage.aspx"), context);
}
else
{
// Returns real page.
return PageParser.GetCompiledPageInstance(context.Request.CurrentExecutionFilePath, fi.FullName, context);
}
}
}
The behaviour I was trying to handle was that if there might be CMS content and I didnt want to have to create a page each time I needed information served, but if a physical page exists that should be returned.
For you, you might want to accept the URL, break it down to the component parts.
so http://www.mywebsite.com/CategoryName/ProductName/1 becomes:
context.Items["Categoryname"] = valueFromUrlasCategoryName;
context.Items["Productname"] = valueFromUrlasProductName;
context.Items["Id"] = valueFromUrlasId (or pg);
return PageParser.GetCompiledPageInstance(url, context.Server.MapPath("~/ControlPage.aspx"), context);
Then on your control page you can intercept these values from the context passed in and interrogate the data as needed.
In the web.config you point a reference towards your HttpHandlerFactory
<httphandlers>
<add path="*.aspx" type="Web.Helpers.HttpCMSHandlerFactory, Web.helpers" verb="*"/>
</httphandlers>
In your case you could set the path as "." to capture all traffic. this would mean you would have to add handling for images and scripts.
you will also need to make sure you add a wildcard to your IIS for extensionless pages.
There are plenty of articles about HttpHandlerFactories on the web, and might explain better.
Two
The sort of thing you are after is part of MVC, could you look at changing your UI to use that?

Related

Need to understand short ASP.NET source code

I am maintaining a customer Classic ASP website, and some ASP.NET code was found in a specific place.
I need someone to help me understand the meaning of each line, because I will have to replace this ASP.NET code with Classic ASP functions.
From my understanding, here is what the code performs :
Get the Request.QueryString url, and put it into a variable named str
Redirect (send a HTTP 302) to the Url-Decoded value of str
I would be sure not missing anything else. Is my understanding full and complete ?
Thank you all .NET folks :)
<%# WebHandler Language="C#" Class="GenericHandler1" %>
using System;
using System.Web;
public class GenericHandler1 : IHttpHandler {
public void ProcessRequest (HttpContext context) {
string str = context.Request.QueryString.Get("url");
// context.Response.Redirect( context.Server.UrlDecode(str));
HttpContext.Current.Response.Redirect(context.Server.UrlDecode(str), false);
}
public bool IsReusable {
get
{
return false;
}
}
}
Your understanding is correct. This is a simple HTTP Handler that decodes URLs and redirects the request to the decoded location.
This is not strictly required in many modern sites but it is a hack that can simplify interpreting url parameters if your site does a lot of it from first principals or in scenarios where you believe the original parameters might be double encoded.
To fully replicate the implementation, you probably don't need to replicate this code at all, not in a global sense. Instead look into the web.config or global.asax.cs or if this is more recent look for startup.cs in one of those files should be the registration for this handler, look for any references to GenericHandler1. When you find that code, you will have found the rest of the implementation detail that you may need to consider implementing.
This is a strange thing to ask, "replicate an ASP.Net website in classic ASP". I'm sure you have your business reasons, but have you instead considered upgrading to an OWIN implementation, perhaps with ASP.Net Core? This is usually the easier option if your requirement is to deploy to non IIS host.

ASP .NET MVC - files access

I'm trying to configure routing in my application in such way: paths to files should work for every directory except one special. And access to files in this direcotory should be processed by my controller action. If I write smth like this:
routes.Map("Specialfolder/{name}", "Controller", "Action");
it works only for unexisting files. Controller doesn't catches route for existing file
If I add this line:
routes.RouteExistingFiles = true;
Working with files in my folder is ok. But files in other directories aren't routed anymore. How to fix this?
Here is a sample ActionFilter sample code snippet that you can use
public class UriActionFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
// Sample: somehow identify the user, in case of a custom identity, replace the below line to get the user identifier
var user = System.Threading.Thread.CurrentPrincipal.Identity.Name;
// get the parameter that will let you know what is the image or path that is being requested now
var ctxParams = filterContext.ActionParameters;
// if the user does not have the permission to view, return 403
filterContext.RequestContext.HttpContext.Response.StatusCode = 403;
return;
}
base.OnActionExecuting(filterContext);
}
}
In this snippet, you have to replace with the corresponding service calls etc..
W.R.TO the OnAuthorization, I was mentioning that you can use so that any one that gets access to the image may hit the URI directly and that can be used filtered. We are using that way to restrict the URI access directly by a given user.
This will help you to override the MVC authorization of each request that the controller gets. This is a more of a restriction by URI alone. However, your case can be satisfied by the above action filter too as parsing of the uri parameters can be quite tricky at times.

How to add a cache buster to all images in an ASP.NET MVC site

I am trying to improve the performance of a large ASP.NET MVC website and I am currently looking for a way of adding a cache busting query string to image requests in such a way that I don't have to go through all the views and CSS and change each image reference.
Desired result
To verify if the cache buster is being added I am using the Firebug extension in Firefox and what I am looking to see is something like this (screenshot taken from another website)
What I've tried
The simplest answer seemed to me to create a custom HttpModule that intercepted any image request and then appended the cache busting query string to the request URL. So I wrote the following class
public class CacheBusterImageHandler : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += this.BeginRequest;
}
public void Dispose()
{
}
public void BeginRequest(object sender, EventArgs e)
{
// This conditional check will need to be modified to capture other image types
// but for testing purposes it is good enough
if (HttpContext.Current.Request.Path.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
{
// The version number will be generated but again for testing purposes this
// is good enough
var pathWithCacheBuster = string.Format("{0}?v1.0.0.0", HttpContext.Current.Request.Path);
HttpContext.Current.RewritePath(pathWithCacheBuster);
}
}
}
I then registered the module in web.config like this
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="CacheBusterImageHandler" type="MyAssembly.CacheBusterImageHandler" preCondition="managedHandler" />
...
I then verified that the requests were getting processed by the module by using breakpoints, however when I checked in Firebug the image requests did not have the cache buster appended to URL. I then decided to read the documentation for the RewritePath method and found that of course it simply redirects the request but does not alter the requested URL.
Questions
Is there a way in an HttpModule to append the cache buster to the query string?
If not, is there some other way I can achieve the same result without having to modify every reference to an image?
"Is there a way in an HttpModule to append the cache buster to the
query string?"
No. That is far to late in the process. The URL has to be changed when you put it in the page, that's what the browser uses to check if the image is in the cache or not.
"If not, is there some other way I can achieve the same result without
having to modify every reference to an image?"
That depends on how you put the image URLs in the page, but there is no way of changingt he URLs that works for all way to put an URL in the page.
You can make a method that calculates the version number/string to include, and add that to all URLs. That way you only have to make the change once, not every time an image changes.
The method could use the version number or compile time of the assembly if you want to invalidate the cache every time that you deploy the page, or the update time of the image file.
Basically:
<img src="/images/logo.png<%= ImageVersion("/images/logo.png") %>" alt="Logo">
Using something like:
public static string ImageVersion(string name) {
FileInfo info = new FileInfo(HttpContect.Current.MapPath(name));
int time = (int)((info.LastWriteTimeUtc - new DateTime(2000,1,1)).TotalMinutes);
return "?v=" + time.ToString();
}

Is there a way to conceal a URL but have it still be valid?

I've looked at both URLRewriting and Redirecting and I'm mostly convinced it's not the way to go.
What I have is a web form that takes in a search string, and then outputs results as links to different PDF files that are hosted on a web share. These files are not local or apart of the web forms at all. When you click a link to a pdf you get the direct web link to the pdf, for example:
http://www.mywebsite.com/portfolios/employees/evaluations/test1.pdf
If a user gets that link and backspaces to:
http://www.mywebsite.com/portfolios/employees
They could potentially have access to all other records. The example is a little bad because I can't apply explicit permissions to the page at hand. So what I'd like to know if it's somehow possible to re-write
http://www.mywebsite.com/portfolios/employees/evaluations/test1.pdf
to something like
http://www.mywbesite.com/test1.pdf
and have it still be valid?
Add a generic handler to your website, lets call it FileRetriever.ashx. This will allow us to use some logic to make sure the user is authorized to access the file they're requesting. This assumes your users have logged into the site (else how will you know whether they are allowed to access the file or not?)
public void ProcessRequest(HttpContext context)
{
if(String.IsNullOrEmpty(context.Request.QueryString["file"]))
{
//return error status code telling them to provide a file
context.Response.End();
}
string requestedfile=context.Request.QueryString["file"];
if(!File.Exists(Server.MapPath(requestedfile)))
{
//write error status code telling them the file doesn't exist. Probably a 404 error.
context.Response.End();
}
if(HasAccess(context.User.Identity.Name, requestedfile))
{
//write the file to the response
context.Response.ContentType= "";//replace with MIME type of requested file
context.Response.WriteFile(context.Server.MapPath(requestedfile));
context.Response.End();
}
else
{
//return error status code telling them they're unauthorized
context.Response.End();
}
}
public static bool HasAccess(string username, string file)
{
//if user has access to the file, return true. Else return false. This might involve database lookups etc
}
The real magic is going to happen in the HasAccess function where you'll have to implement your checks to make sure they're authorized to access the file. All the other stuff is just handling common things like forgetting to specify the file or specifying an invalid file.
User then accesses a file by requesting this url: mysite.com/FileRetriever.ashx?file=path/to/document/document.pdf
You should never and i emphasize ever have sensitive files exposed to the web. Not knowing the url is not a security/authentication method. All the files you wish to protect should live outside of IIS/Web and need to be retrieved and served by your application. The application would control access to the files based on some level of authorization.
The reason for this as it has been seen in many large companies such as AT&T it takes one user to brute force all links on your site and obtain all your employee information just by trying different url combinations. Do not go down this road.

Using Custom HttpHandler to redirect users in Sharepoint

I need to redirect a user to a different page if they visit a certain set of pages in a web. There is one condition: I'm not allowed to touch the IIS. I have googled around and found a thing called the custom HttpHandler for WSS 3.0. It sounded like something that I can use to capture the URL the user enters (the most malicious way to get to a page that should really be redirected) and redirect them to another page/web. But having not have the chance to use it yet, I was wondering am I on the right track by using a Custom HttpHandler to redirect a user to a different page in Sharepoint using C#?
Many thanks.
HttpHandlers are used by IIS to "handle" different document types, for instance you have separate .asmx handle, .aspx handler, .ascx handler, etc.
Look into SPUtility.Redirect
You can use the SPUtility.Redirect method whenever you want to direct the user to a different page. For example, you might create a landing page that determines the user's role membership, and based on that information you can redirect them to an appropriate page. Or, based on the contents of a query string issued by the user's browser, you might redirect them to a page that can process the query string, such as the Search Center results page.
Alternatively, you can look into Using Disposable Windows SharePoint Services Objects.
--EDIT--
This is in response to your comment; you are thinking in the right direction
Create a custom HttpHandler; Example 1, Example 2
Whenever a user requests a url, your custom HttpHandler is going to process that.
Redirect() if you like the url, else otherwise.
But for your Custom HttpHandler to work, it should be called first - so in your config file you will have to provide the value in path. Usually an extension is added here. But you can replace that with a * to work for all request. I believe that would work.
<httpHandlers>
<add verb="*" path="*.aspx" type="MyNameSpace.MyHttpHandler, MyAssemblyName" />
</httpHandlers>
--EDIT--
This is in response to your comment. Assuming that you have "access" to pages so that you can write javascript in it, you can use the javascript in following way.
<script language=JavaScript>
function RedirectIfUnAuthorized()
{
//Get the location of the current page.
var currentUrl = new String( document.location.href )
//See if it belongs to the urls you are looking for; redirect if so.
if (currentUrl == "http://www.thisUrl.com/page1.aspx") {Response.Redirect("http://www.GOOGLE.com")}
if (currentUrl == "http://www.thisUrl.com/page2.aspx") {Response.Redirect("http://www.BING.com")}
if (currentUrl == "http://www.thisUrl.com/page3.aspx") {Response.Redirect("http://www.someother.com")}
}
</script>
You may call the above javascript in the page's OnLoad event.
Are you allowed to deploy code on the server? Or is that touching IIS too?
(SharePoint makes changes to web.config too. If you're allowed to deploy code, so could you. If you don't tell your admins they probably wouldnt even notice.)
You can 'deploy' your web.config changes through an SPWebConfigModification and deploy any web.config redirections or httphandlers that way.
HTTP handlers are the end point
objects in ASP.NET pipeline and an
HTTP Handler essentially processes the
request and produces the response. For
example an ASP.NET Page is an HTTP
Handler.
HTTP Modules are objects which also
participate the pipeline but they work
before and after the HTTP Handler does
its job, and produce additional
services within the pipeline (for
example associating session within a
request before HTTP handler executes,
and saving the session state after
HTTP handler has done its job, is
basically done by an HTTP module,
SessionStateModule)
In your case, HTTPModule will require to redirect the another web page.
using System;
using System.Web;
using System.Web.UI;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data;
namespace CustomHttpModule
{
public class HttpModuleImplementation : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
}
public void Init(HttpApplication context)
{
if (context == null)
throw new ArgumentNullException("Context == null");
context.AuthorizeRequest += new EventHandler(this.ProcessRequestHandler);
}
#endregion
private void DummpRequest(object sender, EventArgs e)
{
}
//first check that user.identity record exist in database
//If not then forward user to User registration page
private void ProcessRequestHandler(object sender, EventArgs e)
{
try
{
HttpApplication context = (HttpApplication)sender;
string strAbsoluteUri = context.Request.Url.AbsoluteUri.ToLower();
//check if request is accessing aspx page
if (strAbsoluteUri.Substring(strAbsoluteUri.Length - 5, 5).Contains(".aspx"))
{
string userName = context.User.Identity.Name;
//replace Test Module with DB call to validate user data
if (!CheckUserInDb(userName))
{
if (!strAbsoluteUri.Contains("mypage.aspx"))
redirectToRegistrationPage(context);
}
}
}
catch (Exception ex)
{
}
}
private void redirectToRegistrationPage(HttpApplication context)
{
context.Response.Redirect("http://" + context.Request.ServerVariables["HTTP_HOST"].ToString() + "Regpage.aspx", false);
}
private bool CheckUserInDb(string userName)
{
return true;
}
}
}
In SharePoint virtual directory web.config file you have to enter the following entry under section httpModules :
<add name="CustomHttpModule" type="CustomHttpModule.HttpModuleImplementation, CustomHttpModule" />

Categories

Resources