I am trying to do the following
I am building asp.net website with c# language
I want to read a text file from my project(the file is inside the project)
I tried to get the file Path by this way :
string path=Request.PhysicalApplicationPath+"filename.txt";
but I can't use The "Request" object from separated C# file ??
note: separated C3 file,I mean it's not related with aspx file
can you help me with my way or do you have another way ??
thx
I would recommend you passing the path to your library from the web application. So for example in your web app:
var path = Server.MapPath("~/filename.txt");
var result = BusinessLayer.SomeMethod(path);
You could also use HostingEnvironment in your class library but I would really advice you against it as it creates a dependency with System.Web which makes your class library tied to a web context and not unit testable friendly:
var path = Path.Combine(
HostingEnvironment.ApplicationPhysicalPath,
"filename.txt"
);
Using HttpContext.Current you have access to Request, Server, Response and other objects of the HTTP request.
but I can't use The "Request" object from separated C# file ??
I'm guessing you mean this is in a dll?
If so, then you can get to it by referencing system.web in the separate dll, and getting at the httpcontext.current object
I would use some sort of injection mechanism either to give the application root path to the class or a copy of the current context/request to the class that it can use. Essentially, you want to give the class the means to find the path (or even give it the path) rather than use a fixed dependency that is hard to recreate in testing. To simplify my example, I'll use the Request as you are doing, though, you could easily provide just the base path of the application as a string as well.
public class Foo
{
// HttpRequestBase may be more appropriate
private HttpRequest Request { get; set; }
public Foo( HttpRequest request )
{
this.Request = request;
}
public void Bar()
{
string path = Path.Combine( this.Request.PhysicalApplicationPath,
"filename.txt" );
...
}
}
Note that you could combine this with #Darin's ideas on how to calculate the server path as well.
Related
I am in .NET MVC, I wish to read a JSON file like so:
JSON = System.IO.File.ReadAllText("companyInfo.json");
However I can't get a path going. I don't care where I put the json file, so I am asking for both a recommended place to put it as well as the path string to use.
you can Try to use the following with asp.net core
public class HomeController : Controller
{
private readonly IHostingEnvironment _hostingEnvironment;
public HomeController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public ActionResult Index()
{
string contentRootPath = _hostingEnvironment.ContentRootPath;
var JSON = System.IO.File.ReadAllText( contentRootPath + "/companyInfo.json");
return null;
}
}
Update
The contentrootPath will give you the base root of your application
Something like this ContentRoot: C:\MyApp\ note it depends on the OS
you can create a data folder and use Path.Combine to build your absolute path
But you can use the WebRootPath
string webRootPath = _hostingEnvironment.WebRootPath;
if you want it to be served from The web root which is the root directory from which static content is served,
An appropriate place to put it is the App_Data folder inside your project root. If this folder doesn't exist, create it. This folder is somewhat special, in that nothing in that folder can be directly requested by a browser; it can only be accessed via code. Getting the path to App_Data is fairly straightforward:
string filePath = HttpContext.Server.MapPath("~/App_Data/companyInfo.json");
Using that path, you can open your file for reading.
You can use any folder you want for this, but you'll need to take steps to prevent the browser from directly requesting the file. ASP.Net does this for you automatically for App_Data.
public void getContent() {
string VirtualPath = "~/Content.aspx";
var page = BuildManager.CreateInstanceFromVirtualPath( VirtualPath, typeof( Page ) ) as IHttpHandler;
page.ProcessRequest( HttpContext.Current );
}
I'm using that function to load the content from different files, but the "page.ProcessRequest( HttpContext.Current )" inserts the content at the current context, and what I need is the function to return the content of the specified file.
I wonder if there's a working way to create a new HttpContext, so that "page.ProcessRequest" don't insert anything into the current response.
Oded is correct as far as I know. You can't easily create your own instance of the HttpContext. However you can still achieve your goals thorugh other means.
Use a Server.Execute. http://msdn.microsoft.com/en-us/library/ms150027.aspx.
You can specify the HttpHandler to execute along with a TextWriter to dump the content into.
You can't create a new HttpContext, not without lots of work arounds.
It is one of the failings of ASP.NET and the BCL - makes web applications untestable (or at least very difficult to test without HttpContext.
I am not clear on your requirement what I need is the function to return the content of the specified file - can you please explain exactly what you mean by that?
Check out Pex/Moles its includes a mocking framework that can mock almost any type or member, even if its sealed or static.
(it does this by using a custom test host)
I have a logging class that, well, logs things. I would like to add the ability to automatically have the current page be logged with the messages.
Is there a way to get the information I'm looking for?
Thanks,
From your class you can use the HttpContext.Current property (in System.Web.dll). From there, you can create a chain of properties:
Request
Url and RawUrl
The underlying object is a Page object, so if you cast it to that, then use any object you would normally use from within a Page object, such as the Request property.
It's brittle and hard to test but you can use System.Web.HttpContext.Current which will give you a Request property which in turn has the RawUrl property.
public static class MyClass
{
public static string GetURL()
{
HttpRequest request = HttpContext.Current.Request;
string url = request.Url.ToString();
return url;
}
}
I tried to break it down a little :)
In the past I've also rolled my own logging classes and used Console.Writeln() but really there are a number of good logging options that already exist so why go there? I use NLog pretty much everywhere; it is extremely flexible with various log output destinations including console and file, lots of log format options, and is trivial to set up with versions targeting the various .net frameworks including compact. Running the installer will add NLog config file options to the Visual Studio Add New Item dialog. Using in your code is simple:
// declare in your class
private static Logger logger = LogManager.GetCurrentClassLogger();
...
// use in your code
logger.Debug(() => string.Format("Url: {0}", HttpContext.Current.Request.Url));
I am in the process of moving all of the images in my web application over to a CDN but I want to easily be able to switch the CDN on or off without having to hard code the path to the images.
My first thought was to add an HttpHandler for image extensions that depending whether a variable in the web.config (something like ) will serve the image from the server or from the CDN. But after giving this a little though I think I've essentially ruled this out as it will cause ASP.NET to handle the request for every single image, thus adding overhead, and it might actually completely mitigate the benefits of using a CDN.
An alternative approach is, since all of my pages inherit from a base page class, I could create a function in the base class that determines what path to serve the files from based off the web.config variable. I would then do something like this in the markup:
<img src='<%= GetImagePath()/image.png' />
I think this is probably what I'll have to end up doing, but it seems a little clunky to me. I also envision problems with the old .NET error of not being able to modify the control collection because of the "<%=" though the "<%#" solution will probably work.
Any thoughts or ideas on how to implement this?
You've dismissed writing an HttpHandler based on an assumption of pre-optimization. I would revisit this and definitely write a simple HttpHandler and test it out. You might find that your Page method solution might even be slower, especially if you get the ASP preprocessor involved.
HttpHandlers are pretty close to the metal - it's a miniscule amount of overhead for IIS to hand the request to ASP.Net. It would be a more elegant solution than what you're proposing, and probably more scalable and I'm willing to bet - faster.
Have you considered a slightly simpler approach?
If your pages all inherit from a base class, you could expose a property on that which contains the prepend URL to your CDN (or, to your local server if you want to switch the CDN off). It is then a trivial matter of storing the prepend URL in the web.config:
public string PrependURLPath() {
get { return ConfigurationManager.AppSettings["ImagePrependURL"].ToString(); }
}
In your <appSettings/> element, you can simply choose what the prepend URL would be, eg:
http://my.cdn.com/user/
or:
http://my.own.server.com/images/
Pretty simple!
You would then be able to code your image refernces as per your example, but calling your base page property to expose the desired path:
<img src='<%= this.BasePage.PrependURLPath() + [YourImagePath.png] %>'/>
I agree that setting the image source through the inline call is messy, but you could probably do as someone else has suggested and then iterate through the image controls on your page, changing the prepend URL as you go.
Even if your pages currently only inherit from System.Web.UI.Page, it's a simple matter to create your own base class which inherits System.Web.Page, then do a find/replace in your solution on all remaining pages.
Hope this helps.
weighing in pretty late here, but i've been looking for a similar solution myself. searched google to sanity check what i had done. didn't consider the HttpHandler approach, what i did was simply extend the ASP.net Image control:
public class Img : Image
{
public Img()
{
RelativePath = false;
}
public bool RelativePath { get; set; }
public override string ImageUrl
{
get
{
if (RelativePath)
return base.ImageUrl;
return "http://some.configurable-value.com" + base.ImageUrl;
}
set { base.ImageUrl = value; }
}
}
it's rough and ready, but it works :) obviously it should rely on some configurable value rather than a string literal, but that's not a big change
If you display your images using tags you could create a control adapter, these allow you to alter the way .net controls render or universally alter them something like this should do the trick:
using System.Web.UI.WebControls.Adapters;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ExampleCode
{
public class ImageAdapter : WebControlAdapter
{
private bool UseCdn
{
get { return true; } // Get value from config or anywhere else
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
Image image = (Image)Control;
if (UseCdn)
{
// If using relative urls for images may need to handle ~
image.ImageUrl = String.Format("{0}/{1}", "CDN URL", image.ImageUrl);
}
}
}
}
Then add a browser file to the App_Browsers folder in your web project like below:
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter
controlType="System.Web.UI.WebControls.Image"
adapterType="ExampleCode.ImageAdapter"
/>
</controlAdapters>
</browser>
</browsers>
you could loop all controls and change the images url in the prerender event on your base class...
The good thing about the HTTP Handler approach is that it's quite re-usable and configurable: you can identify img paths to handle based on location - assuming the structure they're in helps this.
The possible drawback is that image file extensions (.jpg, .png, etc) aren't automatically passed on to the asp.net pipe-line; you can easily config IIS to do so - but you need to have a certain level of contriol over IIS - so it might not be an option if you're on a shared hosting environment.
I will go for #Rhys approach for image control.
Most of the time, I try to use background image css than using image control.
After that I upload both css and images together to the cloud and working fine for relative path.
Doesn't look like there has been an accepted answer yet so here is my suggestion. I had similar problems dealing with modifying URL's transparently (to a different end, but I thought about using it for CDN support as well).
This is an old filter / module but it worked well for my needs with a little tuning: http://www.paraesthesia.com/archive/2007/12/14/urlabsolutifiermodule---convert-urls-in-asp.net-output-to-absolute.aspx
What you can do is make a response filter and hook it with an httpmodule (as this absolutifier does). If you use this module + response filter you could probably achieve what you need by modifying the source for it to replace the hostname / prefix all urls to use the CDN.
I had to solve your problem and another one, that is I do not want to take resources from the CDN during development but only when the website is deployed on the production server.
To solve this I developed an ExpressionBuilder that prepends the CDN URL only in production.
<asp:Image ImageUrl="<%$ CdnUrl:/images/myimage.png %>" runat="server" />
In previous code the CDN URL will be prepended only in production.
namespace IdeaR.Web.Compilation
{
[ExpressionPrefix("CdnUrl")]
public class CdnUrlExpressionBuilder : ExpressionBuilder
{
public static object GetCdnUrl(string expression, Type target, string entry)
{
var retvalue = expression;
var productionUri = new Uri("http://www.myproductionurl.com",
UriKind.Absolute);
var currentUri = HttpContext.Current.Request.Url;
var cdnUrl = "http://cdn.mycdn.com";
// If this is a production website URL
if (currentUri.Scheme == productionUri.Scheme &&
currentUri.Host == productionUri.Host)
retvalue = cdnUrl + expression;
return retvalue;
}
public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
object parsedData, ExpressionBuilderContext context)
{
var componentType = entry.DeclaringType;
var expressionArray = new CodeExpression[3]
{
new CodePrimitiveExpression(entry.Expression.Trim()),
new CodeTypeOfExpression(componentType),
new CodePrimitiveExpression(entry.Name)
};
var descriptor = TypeDescriptor.GetProperties(componentType)
[entry.PropertyInfo.Name];
return new CodeCastExpression(descriptor.PropertyType,
new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(GetType()),
"GetCdnUrl", expressionArray));
}
}
}
For more information I wrote an article on this
How to use a CDN in production but not during development
I created a WebService using the .NET 2.0 framework, a class based on an interface that has the WebServiceAttribute and hosting it using IIS and a ASMX file. The WebService currently loads its configuration from one XML file.
I'd like to create multiple instance of this service where each loads it own configuration.
By coping the ASMX file I can create a clone of the webservice under a different name which will be based on exact the same implementation. But it also loads the exact same configuration file which makes it rather useless.
So my question is: What is the best way to create an arbitrary number of WebServices that are based on one class, living in one IIS virtual directory where each is loading a different configuration file?
Solution
With the help of Pavel Chuchuva's answer I created the following code to handle the loading of the configuration:
public class WebConfigManager
{
public static T Load<T>() where T: new()
{
string location =
HttpContext.Current.Request.PhysicalPath + ".config";
if (HttpContext.Current.Cache[location] is T)
{
return (T)HttpContext.Current.Cache[location];
}
using (Stream s =
new FileStream(location, FileMode.Open, FileAccess.Read))
{
return (T)(HttpContext.Current.Cache[location] =
new XmlSerializer(typeof(T)).Deserialize(s));
}
}
}
// example of the usage of WebConfigManager
public class MyWebService : IMyWebService
{
Config config = WebConfigManager.Load<Config>();
...
Copy and paste .asmx file to create multiple instances of your web service (e.g. Service1.asmx, Service2.asmx and so on).
Load configuration file based on Context.Request.FilePath value:
public string LoadConfig()
{
string configPath = Server.MapPath(this.Context.Request.FilePath + ".xml");
using (XmlReader reader = XmlReader.Create(configPath))
{
// Will read Service1.asmx.xml, Service2.asmx.xml and so on
}
}
I suggest placing the asmx in different folders and placing a web.config in each of those folders with the setting for that specific instance of the web service. This is the easy and fast way
OR
you could use Web Service Enhancements 3.0 and create a WSE router, redirect a calls to a ASMX to that router and let the router forward the call to the right web service instance and pass additional config. This a more complex way of doing it but it enables u to use a single instance of the web service that's picks the right configuration based on the parameters the router passes it.
For more info on WSE3.0 I point you to the MSDN.
Hope this helps!