I have a simple asp.net mvc 4 website with an admin area. I have defined a custom http handler to handle uploads from a plupload script that runs in the admin area. Here is the code for the handler :
public class CategoryImageUploadHandler : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
try
{
HttpPostedFile file = context.Request.Files[0];
var categoryID = context.Request["categoryID"];
var fileName = Path.GetFileName(file.FileName);
var parentPath = HttpContext.Current.Server.MapPath("~/Files/Content");
var targetDir = Path.Combine(parentPath, categoryID);
var targetFile = Path.Combine(targetDir, fileName);
//check if Directory exists
if (Directory.Exists(targetDir))
file.SaveAs(targetFile);
else
{
Directory.CreateDirectory(targetDir);
file.SaveAs(targetFile);
}
context.Response.Write("/"+categoryID+"/"+fileName);
}
catch (Exception ex)
{
context.Response.Write("0");
context.Response.Write(ex.Message);
}
}
public bool IsReusable
{
get { return false; }
}
}
This is sitting in the Handlers/ directory of the main site. This is how I have registered the handler :
<system.webserver>
<add name="CategoryImageUploadHandler path="Admin/CategoryImageUploadHandler.ashx" verb="*" type="Hitaishi.Web.Handlers.CategoryImageUploadHandler, Hitaishi.Web"/>
<system.web>
<httpHandlers>
<add path="Admin/CategoryImageUploadHandler.ashx" verb="*" type="Hitaishi.Web.Handlers.CategoryImageUploadHandler, Hitaishi.Web"/>
Routeconfig.cs:
routes.IgnoreRoute("{*allashx}", new { allashx = #".*\.ashx(/.*)?" });
However, when the plupload sends a POST to the http handler from the Admin area, the call is still picked up by routing as it tries to look for /Admin/CategoryImageUploadHandler.ashx
I have tried playing with slashes to check if the path I am giving is wrong or changing path in the registrations, but nothing seems to work. I am still getting 404 errors.
In a nutshell, I need a way to reference a HttpHandler defined in the main MVC area of the website from another mvc area of the website. Can anyone help with this?
why are you not using routes.MapPageRoute
so probably you can do
routes.MapPageRoute(
"CategoryImageUploaded",
"Admin/CategoryImageUploadHandler.ashx",
"~/Admin/CategoryImageUploadHandler.ashx")
//please verify you path to .ashx before trying
Related
I want create Handler which collect all URL, I check URL and then continue in "process". That means, if URL is www.mysite.com/Contact it call my handler and it continue in www.mysite.com/Contact. I don't want to call Redirect or something similar.
Problem is, it not pass content and scripts. That means all css, js and image files are not passed. For example I have empty ProcessRequest(HttpContext context) function and it block the files. Question is how to continue (load all files)?
This is how I register my handler
<system.webServer>
<handlers>
<add name="MyHandler" path="*" verb="*" type="MyWebApp.MytHandler" resourceType="Unspecified" preCondition="integratedMode" />
</handlers>
</system.webServer>
I tried this
public void ProcessRequest(HttpContext context)
{
context.Response.Flush();
}
but it still block all files.
If you asking why I want to do this, because I want check Url and if everything is ok, it will continue, if not I redirect to controller. It is easy check Url and redirect to some controll or page, there are tons of examples with context.Response. Maybe I am doing it wrong and instead Handler there is something better.
I resolve it, but I am not sure if it is ok.
public void ProcessRequest(HttpContext context)
{
string page = context.Request.Url.PathAndQuery;
bool continue = true;
if ( page.Contains("Content/") == true
|| page.Contains("Scripts/") == true
|| page.Contains(".ico") == true
)
{
continute = false;
if (page.Contains(".css") == true)
{
context.Response.ContentType = "text/css";
context.Response.TransmitFile(page);
}
else if (page.Contains(".js") == true)
{
context.Response.ContentType = "text/javascript";
context.Response.TransmitFile(page);
}
else
{
// interesting that pictures and audio files doesn't need set ContentType
context.Response.TransmitFile(page);
}
context.Response.Flush(); // don't know if this is needed
}
// ...
// redirection if page is not ok
// ...
}
Create a Deligating handler. and assign handler on application start.
use below code
public class SessionInjectionHandler : DelegatingHandler
{
ContextHeaders contextHeaders = HttpContext.Current.GetContextHeaders();
replace context code here
}
How can we stop logging specific module such as RequestTrackingTelemetryModule in ASP.NET MVC (.NET Full, not .NET Core)
I have tried to remove
<Add Type="Microsoft.ApplicationInsights.Web.RequestTrackingTelemetryModule, Microsoft.AI.Web">
in ApplicationInsights.config but it throws me a strange exception
I am using Azure Web App for both staging and live environments. So I just want to stop logging request information on live environment since it cost too much.
Thanks for helping!
You can use ITelemetryProcessor, and following this link.
Add a custom class which implements ITelemetryProcessor:
public class MyTelemetryProcessor : ITelemetryProcessor
{
private ITelemetryProcessor Next { get; set; }
public MyTelemetryProcessor(ITelemetryProcessor next)
{
this.Next = next;
}
public void Process(ITelemetry telemetry)
{
RequestTelemetry request = telemetry as RequestTelemetry;
if (request != null)
{
return;
}
if (request == null)
{
this.Next.Process(telemetry);
}
}
}
Then in the ApplicationInsights.config, add this:
<TelemetryProcessors>
<Add Type="WebApplicationMVC.MyTelemetryProcessor, WebApplicationMVC">
<!-- Set public property -->
</Add>
</TelemetryProcessors>
the screenshot:
It did filter out all the requests from app insights data, test result as below:
I'm trying to implement an IVirtualImageProvider plugin for ImageResizer as explained here. I didn't find the instructions hard to follow, however it doesn't seem like any of my images are passing through the plugin. My images are stored in a Windows folder located outside of the ASP .NET root.
Any image path that starts with "assets" or "images" should be handled by the plugin. Here is my implementation of the IVirtualImageProvider and IVirtualFile interfaces:
namespace ImageResizer.Plugins.Basic
{
public class ResizerVirtFolder : IPlugin, IVirtualImageProvider
{
public IPlugin Install(Configuration.Config c)
{
c.Plugins.add_plugin(this);
return this;
}
public bool Uninstall(Configuration.Config c)
{
c.Plugins.remove_plugin(this);
return true;
}
public bool FileExists(string virtualPath, System.Collections.Specialized.NameValueCollection queryString)
{
return (virtualPath.StartsWith("assets", StringComparison.OrdinalIgnoreCase) || virtualPath.StartsWith("images", StringComparison.OrdinalIgnoreCase));
}
public IVirtualFile GetFile(string virtualPath, System.Collections.Specialized.NameValueCollection queryString)
{
return new ResizerVirtualFile(virtualPath);
}
}
public class ResizerVirtualFile : IVirtualFile
{
public ResizerVirtualFile(string virtualPath)
{
this._virtualPath = virtualPath;
}
protected string _virtualPath;
public string VirtualPath
{
get { return _virtualPath; }
}
public System.IO.Stream Open()
{
string sitePath = System.Configuration.ConfigurationManager.AppSettings["PageFilesLocation"];
_virtualPath = _virtualPath.Contains("assets/") ? _virtualPath.Substring(_virtualPath.IndexOf("assets/") + 7) : _virtualPath;
string assetPath = Path.Combine(sitePath, _virtualPath.TrimStart('/').Replace("/", #"\"));
System.IO.FileStream oStream = new FileStream(assetPath, FileMode.Open);
return oStream;
}
}
}
Here's a brief snippet of the Web.config modification I made for the plugin:
<resizer>
<plugins>
<add name="MvcRoutingShim" />
<add name="ResizerVirtFolder" />
</plugins>
</resizer>
ImageResizer.Plugins.Basic.ResizerVirtFolder shows up under registered plugins when I go to resizer.debug.ashx, so I believe that means the plugin is loaded. However, when I put a breakpoint on the FileExists or GetFile functions, it isn't triggered.
I thought to use the VirtualFolder plugin, but it doesn't look like it's included in the download any more. I'm using v 3.4.3.
Edit: Added link to the debug output Gist here.
Longer Edit: I should add that the images that are not showing up do not have query strings in their requests and are not being resized in any way. Does that mean that ImageResizer will not look at them at all, and as a result, the Virtual Image Provider's functions will not be executing in this case?
Another Edit: Looking at this page, it seems like the simplest way to get the images to work in ImageResizer might be to add a different prefix rather than /assets or /images, like perhaps /resize. In this case, should I add an ignore route for /resize or not? There is a route handler provided by my CMS which will eventually try to deal with this route if I do not ignore it.
Well, looks like I found my own solution. Here's the problem:
return (virtualPath.StartsWith("assets", StringComparison.OrdinalIgnoreCase)
StartsWith will always return false because the virtualPath will start with my hostname. Switching to a Contains() statement has this working perfectly.
Great plugin, by the way!
I am a newbie in asp mvc, and would like to define links in views to image/content folder in a way so I don't have to change each link if a image folder changes.
Is is possible using ActionLink and routing, bundling or there is a better way to achieve this.
I could not find a good example anywhere so I did not try anything any coding far.
I am thinking of storing a fixed path somewhere, but is that really a mvc type solution?
There are a number of ways you could do this. Here's one approach to extend the Url.Content() method.
1. Create an extension method
We'll called it Virtual().
namespace TestApp.Extensions
{
public static class UrlHelperExtensions
{
private const string _settingPattern = "path:{0}";
private const string _regexPattern = #"\{\w+\}";
public static string Virtual(this UrlHelper helper, string url)
{
Regex r = new Regex(_regexPattern);
var matches = r.Matches(url);
if (matches.Count == 0) return url;
var sb = new StringBuilder(url);
var keys = WebConfigurationManager.AppSettings.AllKeys;
foreach (var match in matches)
{
string key = match.ToString().TrimStart('{').TrimEnd('}');
string pattern = string.Format(_settingPattern, key);
foreach (var k in keys)
{
if (k == pattern)
{
sb.Replace(match.ToString(), WebConfigurationManager.AppSettings.Get(k));
}
}
}
return helper.Content(sb.ToString());
}
}
}
2. Add settings to the main Web.config
Freely add any paths you want.
<add key="path:images" value="~/Content/images" />
<add key="path:scripts" value="~/scripts" />
3. Add the namespace to the Web.config of your views directory
<namespaces>
<add namespace="TestApp.Extensions"/>
</namespaces>
4. Use the new method
#Url.Virtual("{images}/mypic.png")
Output:
/Content/images/mypic.png
You can now use Virtual() where you would Content().
This solution is arguably excessive, but it is comprehensive.
When I serve a javascript file to a user from the /Content Directory I want to replace a string token in that file with a value, so that when the user requests a given file, it has all the customizations they expect.
I think that means I need to somehow proxy requests to the /Content directory, perform the dynamic insertion, and give the file to the user.
I'm interested in performing this insertion as a stream or as a in -memory file. I'd prefer to use a stream just because it's probably more efficient memory wise.
How do I get ASP.NET to proxy this directory?
I've attempted
Using routes to point to a controller
WCF to proxy a URL
But they all seem "ugly" to me and I'd like to make this insertion/replacement as transparent as possible int he project.
Is there a cleaner way?
The easiest way is to create an action on a controller.
public class JavascriptController : Controller
{
public ActionResult Load(string file)
{
var content = System.IO.File.ReadAllText(Server.MapPath(string.Format("~/Content/{0}", file)));
//make replacements io content here
return this.Content(content, "application/javascript");
}
}
You can then access the javascript like this (assuming you have the default routing):
http://localhost:53287/Javascript/Load?file=file.js
where file.js is the name of the file you are requesting.
Don't worry about the url, you can customise this by creating another route if necessary
Here is alternative answer to the answer I posted above, taking into account your comment regarding dynamic javascript.
Firstly, I don't know of a way to do this specifically using either mvc or wcf.. the only way I know how to do this is with a lower-level HttpModule
Take a look at the following code:
public class JavascriptReplacementModule : IHttpModule
{
public class ResponseFilter : MemoryStream
{
private Stream outputStream = null;
public ResponseFilter(Stream output)
{
outputStream = output;
}
public override void Flush()
{
base.Flush();
this.Seek(0, SeekOrigin.Begin);
var sr = new StreamReader(this);
string contentInBuffer = sr.ReadToEnd();
//Do replacements here
outputStream.Write(UTF8Encoding.UTF8.GetBytes(contentInBuffer), 0, UTF8Encoding.UTF8.GetByteCount(contentInBuffer));
outputStream.Flush();
}
protected override void Dispose(bool disposing)
{
outputStream.Dispose();
base.Dispose(disposing);
}
}
public void Dispose() { }
public void Init(HttpApplication context)
{
context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
}
void context_PostRequestHandlerExecute(object sender, EventArgs e)
{
var context = (HttpApplication)sender;
if (context.Request.Url.AbsolutePath.StartsWith("/Content") && context.Request.Url.AbsolutePath.EndsWith(".js"))
{
HttpContext.Current.Response.Filter = new ResponseFilter(HttpContext.Current.Response.Filter);
}
}
}
And register the module like this (make sure you put the full type in the type attribute):
<system.webServer>
<modules>
<add name="JavascriptReplacementModule" type="JavascriptReplacementModule"/>
</modules>
</system.webServer>
This allows you to modify the output stream before it gets to the client