I'm trying to execute a virtual path inside a HTTP module (I want to capture the result).
But I get an error:
Error executing child request for /home/something/.
Code:
public void ExecuteUrl(string url)
{
var sb = new StringBuilder();
var sw = new StringWriter(sb);
HttpContext.Current.Server.Execute(url, sw);
return sb.ToString();
}
Another related question says that it's by design, and the accepted answer won't work for me since I want to capture the result.
Is there another approach that I can take?
Related
I have a .NET 7 web app, where I have a controller that results in a sitemap.xml file. When I run the application locally, I get an XML file as a result with this content:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"/>
And it looks like this:
However, when this is pushed to production (everything is hosted as a web app on Azure), the same endpoint returns nothing. It does recognize the endpoint and looks like this:
My code to generate this, is shown below:
[Route("/sitemap.xml")]
public async Task SitemapXml()
{
var countries = await _countryService.GetBySpecificationAsync(new CountrySpecification()
{
Take = int.MaxValue
});
Response.ContentType = "application/xml";
using (var xml = XmlWriter.Create(Response.Body, new XmlWriterSettings { Indent = true }))
{
xml.WriteStartDocument();
xml.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
xml.WriteEndElement();
}
}
My question:
I am completely lost. At first I thought it was because I didn't add support for static files and this is considered a static file, but I do have:
app.UseStaticFiles();
In the Program.cs.
Any hints where I should be starting?
I spent some time this week wanting to answer this question, and I have time now.
The main issue with your attempt is you are not returning XML results. To do so I suggest using IActionResult interface.
Now time to create sitemap.xml. IMO there are 2 ways to go from here, either using a library OR writing your own sitemap method.
I will start with a library. For instance, there is a very simple library (NuGet) called SimpleMvcSitemap.Core. Install it in your project, and in your controller insert the following code:
[Route("/sitemap.xml")]
public async Task<IActionResult> SitemapXml()
{
// your await call etc
List<SitemapNode> nodes = new List<SitemapNode>
{
new SitemapNode(Url.Action("Index","Home")),
new SitemapNode(Url.Action("About","Home")),
//other nodes
};
return new SitemapProvider().CreateSitemap(new SitemapModel(nodes));
}
Btw for this test, I created an asp.net MVC .net 7 project.
I have deployed the solution to azure and it works both on local development and on azure. Here is the result:
If you do want to do it manually, you can do following
var listUrls = new List<string>
{
Url.Action("Index", "Home"),
Url.Action("About", "Home")
};
return new SitemapResult(listUrls);
And here is the implementation:
public class SitemapResult : ActionResult
{
private readonly IEnumerable<string> _urls;
public SitemapResult(IEnumerable<string> urls)
{
_urls = urls;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var response = context.HttpContext.Response;
response.ContentType = "application/xml; charset=utf-8";
var settings = new XmlWriterSettings() { Async = true, Encoding = Encoding.UTF8, Indent = false };
using (var writer = XmlWriter.Create(response.Body, settings))
{
WriteToXML(writer);
await writer.FlushAsync();
}
}
private void WriteToXML(XmlWriter writer)
{
writer.WriteStartDocument();
// Write the urlset.
writer.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
// url element
foreach (var item in _urls)
{
writer.WriteStartElement("url");
// loc
writer.WriteStartElement("loc");
writer.WriteValue(item);
writer.WriteEndElement();
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
The manual way is also deployed on azure and works, but in the manual way you need to do a lot of work that is already done in a library. To be fair both above outcome is inspired form the question How to dynamically create a sitemap.xml in .NET core 2?.
from this msdn magazine: "A controller that returns void will produce an EmptyResult." I assume this holds true also for Task.
So maybe you need to change your return type of your method from Task to Task<IActionResult> (or whatever suits you most) and return the content with any of these availablle methods.
Then though, I cannot understand why without these mods is currently working locally.
This question already has answers here:
The name XXXX does not exist in the current context
(4 answers)
Closed 6 years ago.
I have an application where all of the code is in a singe file, so I'm looking at tidying it up and create separate classes for some of the re-occurring code instead of having the same code duplicated throughout the application. One such action that's duplicated a lot is setting up a WebClient and setting a proxy to do things like download images, check for app updates etc.
I've created a separate 'Proxy.cs' file and added the following code:
class Proxy
{
public static WebClient setProxy()
{
WebClient wc = new WebClient();
wc.Proxy = null;
if (Properties.Settings.Default.useProxy == true)
{
WebProxy proxy = new WebProxy(Properties.Settings.Default.proxyAddress);
if (Properties.Settings.Default.proxyAuth == true)
{
proxy.Credentials = new NetworkCredential(Properties.Settings.Default.proxyUser, Properties.Settings.Default.proxyPass);
proxy.UseDefaultCredentials = false;
proxy.BypassProxyOnLocal = false;
}
wc.Proxy = proxy;
}
return wc;
}
}
The idea being that when I check for updates, download new images etc, I can just call this class each time to configure the WebClient/Proxy. However I cannot seem to get it working. In my main application, I'm calling it like so:
Proxy.setProxy();
byte[] bytes = wc.DownloadData(URL);
However I get the following error in my main application:
The name 'wc' does not exist in the current context
I'm still fairly new to C# and can;t work out how to actually get this working. Any pointers appreciated.
You can try this
WebClient wc = Proxy.setProxy();
byte[] bytes = wc.DownloadData(URL);
Is there a way to synchronously process an uploaded file POSTed to a controller in the ASP.Net Web API?
I've tried the process Microsoft proposed here, and it works as described, but I'd like to return something other than a Task<> from the Controller method in order to match the rest of my RESTful API.
Basically, I'm wondering if there is there any way to make this work:
public MyMugshotClass PostNewMugshot(MugshotData data){
//get the POSTed file from the mime/multipart stream <--can't figure this out
//save the file somewhere
//Update database with other data that was POSTed
//return a response
}
Again, I have made the asynchronous example work but am hoping for a way to process the uploaded file before responding to the client.
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var appData = HostingEnvironment.MapPath("~/App_Data");
var folder = Path.Combine(appData, Guid.NewGuid().ToString());
Directory.CreateDirectory(folder);
var provider = new MultipartFormDataStreamProvider(folder);
var result = await Request.Content.ReadAsMultipartAsync(provider);
if (result.FileData.Count < 1)
{
// no files were uploaded at all
// TODO: here you could return an error message to the client if you want
}
// at this stage all files that were uploaded by the user will be
// stored inside the folder we specified without us needing to do
// any additional steps
// we can now read some additional FormData
string caption = result.FormData["caption"];
// TODO: update your database with the other data that was posted
return Request.CreateResponse(HttpStatusCode.OK, "thanks for uploading");
}
}
You might notice that the uploaded files are stored inside the specified folder with names that might look like this: BodyPart_beddf4a5-04c9-4376-974e-4e32952426ab. That's a deliberate choice that the Web API team made that you could override if you want.
Can I set up HTML/Email Templates in C# on ASP.NET?
This question was asked and answered by SkippyFire and others...I have a follow up question. I like to keep things very simple, as a novice developer.
If I am not correct, Skippyfire said you could send the complete aspx page using this code:
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
this.Render(htmlTW);
Then just use net.mail to send on Page.Load event. This is very confusing to me. I can use this to render controls to an email.Body and send but I can not use this to load an entire page in anyway I have discovered.
Using Net.mail...
How would I send the page above? I tried to put nothing on the page but some text and send it using it's own page load event... I can not figure out any other way to send it from another page or button... (how would you do this? Wouldn't you have to somehow load the URL into an object?)... anyway I tried to do it from Page Load itself as Skippyfire describes in an old post and get this error from Visual studio IDE:
A page can have only one server-side Form tag.
Any help would be appreciated.
CS
It would be sometihng like this:
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter htmlTW = new HtmlTextWriter(sw))
{
this.Render(htmlTW);
}
using (var message = new MailMessage
{
From = new MailAddress("from#company.com"),
Subject = "This is an HTML Email",
Body = sw.ToString(),
IsBodyHtml = true
})
{
message.To.Add("toaddress1#company.com,toaddress2#company.com");
SmtpClient client = new SmtpClient();
client.Send(message);
}
}
There is another way to do this... You can host the ASP.Net runtime in your application. It's not major difficult either, this is "sort-of" what you need to do...
First step is to create a remote-able object that will be used to communicate with the domain. It's only needed method is one to return the output of a page:
internal class RemoteAspDomain : MarshalByRefObject
{
public string ProcessRequest(string page, string query)
{
using (StringWriter sw = new StringWriter())
{
SimpleWorkerRequest work = new SimpleWorkerRequest(page, query, sw);
HttpRuntime.ProcessRequest(work);
return sw.ToString();
}
}
}
Then when your ready to create/work with ASP.Net you setup the environment like:
public static string RunAspPage(string rootDirectory, string page, string query)
{
RemoteAspDomain host;
try
{
host = (RemoteAspDomain)ApplicationHost.CreateApplicationHost(typeof(RemoteAspDomain), "/", rootDirectory);
return host.ProcessRequest(page, query);
}
finally
{
ApplicationManager.GetApplicationManager().ShutdownAll();
System.Web.Hosting.HostingEnvironment.InitiateShutdown();
host = null;
}
}
Now you should be able to use it with the following:
string response = RunAspPage("C:\\MyWebAppRoot\\", "/default.aspx", "asdf=123&xyz=123");
Obviously, you don't want to do this for every request as it takes time to perform the startup-shutdown operations. Simply refactor the RunAspPage to be an IDisposable class that destroys the environment on dispose instead of using the finally block.
Update, BTW if Your already running in an ASP.Net session, there are far easier ways to do this. See HttpServerUtility.Execute Method (String, TextWriter)
Please Note: The above code was copy/pasted and simplified from a working copy, I think I got everything you need but my actual implementation is much more complicated. If you have any trouble there are several real-world examples of these API on the internet.
Given an absolute URI/URL, I want to get a URI/URL which doesn't contain the leaf portion. For example: given http://foo.com/bar/baz.html, I should get http://foo.com/bar/.
The code which I could come up with seems a bit lengthy, so I'm wondering if there is a better way.
static string GetParentUriString(Uri uri)
{
StringBuilder parentName = new StringBuilder();
// Append the scheme: http, ftp etc.
parentName.Append(uri.Scheme);
// Appned the '://' after the http, ftp etc.
parentName.Append("://");
// Append the host name www.foo.com
parentName.Append(uri.Host);
// Append each segment except the last one. The last one is the
// leaf and we will ignore it.
for (int i = 0; i < uri.Segments.Length - 1; i++)
{
parentName.Append(uri.Segments[i]);
}
return parentName.ToString();
}
One would use the function something like this:
static void Main(string[] args)
{
Uri uri = new Uri("http://foo.com/bar/baz.html");
// Should return http://foo.com/bar/
string parentName = GetParentUriString(uri);
}
Thanks,
Rohit
Did you try this? Seems simple enough.
Uri parent = new Uri(uri, "..");
This is the shortest I can come up with:
static string GetParentUriString(Uri uri)
{
return uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Segments.Last().Length);
}
If you want to use the Last() method, you will have to include System.Linq.
There must be an easier way to do this with the built in uri methods but here is my twist on #unknown (yahoo)'s suggestion.
In this version you don't need System.Linq and it also handles URIs with query strings:
private static string GetParentUriString(Uri uri)
{
return uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Segments[uri.Segments.Length -1].Length - uri.Query.Length);
}
Quick and dirty
int pos = uriString.LastIndexOf('/');
if (pos > 0) { uriString = uriString.Substring(0, pos); }
Shortest way I found:
static Uri GetParent(Uri uri) {
return new Uri(uri, Path.GetDirectoryName(uri.LocalPath) + "/");
}
PapyRef's answer is incorrect, UriPartial.Path includes the filename.
new Uri(uri, ".").ToString()
seems to be cleanest/simplest implementation of the function requested.
I read many answers here but didn't find one that I liked because they break in some cases.
So, I am using this:
public Uri GetParentUri(Uri uri) {
var withoutQuery = new Uri(uri.GetComponents(UriComponents.Scheme |
UriComponents.UserInfo |
UriComponents.Host |
UriComponents.Port |
UriComponents.Path, UriFormat.UriEscaped));
var trimmed = new Uri(withoutQuery.AbsoluteUri.TrimEnd('/'));
var result = new Uri(trimmed, ".");
return result;
}
Note: It removes the Query and the Fragment intentionally.
new Uri(uri.AbsoluteUri + "/../")
Get segmenation of url
url="http://localhost:9572/School/Common/Admin/Default.aspx"
Dim name() As String = HttpContext.Current.Request.Url.Segments
now simply using for loop or by index, get parent directory name
code = name(2).Remove(name(2).IndexOf("/"))
This returns me, "Common"
Thought I'd chime in; despite it being almost 10 years, with the advent of the cloud, getting the parent Uri is a fairly common (and IMO more valuable) scenario, so combining some of the answers here you would simply use (extended) Uri semantics:
public static Uri Parent(this Uri uri)
{
return new Uri(uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Segments.Last().Length - uri.Query.Length).TrimEnd('/'));
}
var source = new Uri("https://foo.azure.com/bar/source/baz.html?q=1");
var parent = source.Parent(); // https://foo.azure.com/bar/source
var folder = parent.Segments.Last(); // source
I can't say I've tested every scenario, so caution advised.