Maybe a stupid question. C# 6.0 allows for string replacement using this syntax: $"string content {foo} {bar}". I would like to imitate this behavior in a class I've written for strings passed to it by default. The problem is that I am not sure how to access the public properties/variables. I am not sure if there is a way to access the properties using reflection or by passing this or this.Page to the constructor.
Figured it out:
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
object UID = GetPropValue(System.Web.HttpContext.Current.Handler, "UID");
Related
I have a webforms project, and am attempting to run some code that allows me to make a call to an MVC route and then render the result within the body of the web forms page.
There are a couple of HttpResponse/Request/Context wrappers which I use to execute a call to an MVC route, e.g.:
private static string RenderInternal(string path)
{
var responseWriter = new StringWriter();
var mvcResponse = new MvcPlayerHttpResponseWrapper(responseWriter, PageRenderer.CurrentPageId);
var mvcRequest = new MvcPlayerHttpRequestWrapper(Request, path);
var mvcContext = new MvcPlayerHttpContextWrapper(Context, mvcResponse, mvcRequest);
lock (HttpContext.Current)
{
new MvcHttpHandlerWrapper().PublicProcessRequest(mvcContext);
}
...
The code works fine for executing simple MVC routes, for e.g. "/Home/Index". But I can't specify any query string parameters (e.g. "/Home/Index?foo=bar") as they simply get ignored. I have tried to set the QueryString directly within the RequestWrapper instance, like so:
public class MvcPlayerHttpRequestWrapper : HttpRequestWrapper
{
private readonly string _path;
private readonly NameValueCollection query = new NameValueCollection();
public MvcPlayerHttpRequestWrapper(HttpRequest httpRequest, string path)
: base(httpRequest)
{
var parts = path.Split('?');
if (parts.Length > 1)
{
query = ExtractQueryString(parts[1]);
}
_path = parts[0];
}
public override string Path
{
get
{
return _path;
}
}
public override NameValueCollection QueryString
{
get
{
return query;
}
}
...
When debugging I can see the correct values are in the "request.QueryString", but the values never get bound to the method parameter.
Does anyone know how QueryString values are used and bound from an http request to an MVC controller action?
It seems like the handling of the QueryString value is more complex than I anticipated. I have a limited knowledge of the internals of the MVC Request pipeline.
I have been trying to research the internals myself and will continue to do so. If I find anything I will update this post appropriately.
I have also created a very simple web forms project containing only the code needed to produce this problem and have shared it via dropbox: https://www.dropbox.com/s/vi6erzw24813zq1/StackMvcGetQuestion.zip
The project simply contains one Default.aspx page, a Controller, and the MvcWrapper class used to render out the result of an MVC path. If you look at the Default.aspx.cs you will see a route path containing a querystring parameter is passed in, but it never binds against the parameter on the action.
As a quick reference, here are some extracts from that web project.
The controller:
public class HomeController : Controller
{
public ActionResult Index(string foo)
{
return Content(string.Format("<p>foo = {0}</p>", foo));
}
}
The Default.aspx page:
protected void Page_Load(object sender, EventArgs e)
{
string path = "/Home/Index?foo=baz";
divMvcOutput.InnerHtml = MvcWrapper.MvcPlayerFunctions.Render(path);
}
I have been struggling with this for quite a while now, so would appreciate any advice in any form. :)
MVC framework will try to fill the values of the parameters of the action method from the query string (and other available data such as posted form fields, etc.), that part you got right. The part you missed is that it does so by matching the name of the parameter with the value names passed in. So if you have a method MyMethod in Controller MyController with the signature:
public ActionResult MyMethod(string Path)
{
//Some code goes here
}
The query string (or one of the other sources of variables) must contain a variable named "Path" for the framework to be able to detect it. The query string should be /MyController/MyMethod?Path=Baz
Ok. This was a long debugging session :) and this will be a long response, so bear with me :)
First how MVC works. When you call an action method with input parameters, the framework will call a class called "DefaultModelBinder" that will try and provide a value for each basic type (int, long, etc.) and instance of complex types (objects). This model binder will depend on something called the ValueProvider collection to look for variable names in query string, submitted forms, etc. One of the ValueProviders that interests us the most is the QueryStringValueProvider. As you can guess, it gets the variables defined in the query string. Deep inside the framework, this class calls HttpContext.Current to retrieve the values of the query string instead of relying on the ones being passed to it. In your setup this is causing it to see the original request with localhost:xxxx/Default.aspx as the underlying request causing it to see an empty query string. In fact inside the Action method (Bar in your case) you can get the value this.QueryString["variable"] and it will have the right value.
I modified the Player.cs file to use a web client to make a call to an MVC application running in a separate copy of VS and it worked perfectly. So I suggest you run your mvc application separately and call into it and it should work fine.
I'm teaching myself ASP.NET MVC, and am trying to figure out where best to put a function which takes in an Models.Address instance and returns an IHtmlString instance which reads something like:
Line 1<br />
Line 2<br />
City, State
The string could be used in many places throughout my project, so I don't want to write it in one view and have to keep copy-pasting it or attaching that view: By the same logic attaching it to ViewData seems like a bad idea. However, the HtmlEncode method requires an instance of server, which means I can't add the function to the model class either.
Is there a right place to put this - some sort of shared view? (does this belong in the master?) Or am I going about this all wrong?
My current best idea is to create a ViewModel with the method public IHtmlString FormatAddress(Address address, HttpServerUtility server), but I don't know if that's the ASP.NET MVC way to do it.
As this is presentation UI logic, the best place to put it is in a HtmlHelper class.
public static class HtmlHelper
{
public static HtmlString FormatAddress (this HtmlHelper, Address address)
{
string formattedAddress = // the formatted address...
return HtmlString.Create(formattedAddress);
}
}
then, from any view you simply call:
<%= Html.FormatAddress(address) %>
I usually create something like that as an Extension Method on a HelperExtensions class and then place that class in a Helpers folder at the top level of the site.
public static class HelperExtensions
{
public static Format(this Address address, HttpServerUtility server)
{
// Do the work to format here.
}
}
I'm building a site on the new Razor engine that comes with MVC 3 (and loving the new syntax!). However, I am at a loss about using public properties / constants with it. I know that with WebForms we could add a public property in code behind:
public string ImageFolder { get; set; }
I would like to define important variables in one global place that my views can access, starting with paths to CSS files and images:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
var ContentFolder = "~/Content";
var CssFolder = ContentFolder + "/Stylesheets";
var ImageFolder = ContentFolder + "/Images";
}
I have tried putting the above code block in _Layout, as well as inside _ViewStart. However, accessing them from child views fails miserably. I thought of defining a public property in the above code block but it doesn't compile.
Solutions?
As far as I have seen, noone uses code behind with Razor.
I guess I should be able to inherit from the default view and define my properties there (as described on Stack).
But I'm strongly hoping that there should be an easier way to do something so simple?
I decided to follow yet another path, and extended UrlHelper to provide paths to all three folders I think I might need:
public static class ExtensionMethods
{
private const string ImagesFolder = "~/Images";
private const string StylesheetsFolder = "~/Stylesheets";
private const string ScriptsFolder = "~/Scripts";
public static string Images(this UrlHelper url)
{
return url.Content(ImagesFolder);
}
public static string Stylesheets(this UrlHelper url)
{
return url.Content(StylesheetsFolder);
}
public static string Scripts(this UrlHelper url)
{
return url.Content(ScriptsFolder);
}
}
All good to go... almost :-) I'm now wondering if there's a place where I would be able to define the using MyNamespace.Helper statement would go in order for these extension methods to be available application-wide. In the old days we would add an entry in web.config:
<system.web>
<pages>
<namespaces>
<add namespace="MyNamespace.Helper"/>
</namespaces>
</pages>
</system.web>
This doesn't seem to work with Razor :-( I tried adding a using statement in _ViewStart.cshtml but no luck either - the only way for my extension methods to be visible is to add a using statement on a particular page, which again isn't ideal.
Any suggestions? Have any of you seen an explanation of Razor's order of page parsing & delivery?
Your can create a folder "App_Code" and create a file "GlobalVal.cshtml".
bellow is a sample code in the file:
#functions{
public static readonly string __siteHome = "http://www.example.com";
public static readonly string __siteResource = "http://resource.example.com";
}
and bellow is a sample using it:
#GlobalVal.__siteHome
Use the PageData property:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
PageData.Add("ContentFolder", "~/Content");
}
and inside _Layout.cshtml:
<%=PageData["ContentFolder"]%>
In _layout view
#{
App.AnyName = "abc";
}
In Inherit view
#{
var anyVariable = App.AnyName;
}
Just place the constants in a public module inside your app_code folder, or if you don't want to do that just create a clasas in app_code and use the using (imports) keyword to import the namespace (class name) in each view and you can use it that way.
Alternatively, if it makes sense to do so, just add them in your view model - remember, it might not make sense to add those vars to your model, but it can make sense to add them to your view model! This is what the view model is for, and this view model can grab the constant values from a public module or class or you can even set it in your actual view model itself, this way you will only define the values in one place and you don't need to use any namespace imports into each view :)
Let me know how it goes and if there is anything else I can do to help you out.
In vb.net but same as csharp and its easy to understand since it's vb.
Public class YourModel
// this is where you have the normal model you have... No big deal
End Class
...
// now you make the view model urself
...
Public class MyViewModel
Public MyNormalModel as YourModel
//notice we r declaring ur normal model as a variable, u can use a property instead
Public MyPathConstant1 as string = "abc"
Public MyPathConstant2 as string = "abc"
Public MyPathConstant3 as string = "abc"
End Class
Now, you gotta set the value of MyNormalModel to ur current model instance, although you can do that in ur controller, it's best practice to create a method inside the MyViewModel class that takes a copy of ur current model as argument and does the setting of MyNormalModel to the current model we just passed in the argument.
You can still make that call in your controller, but on another note, what people prefer to do is, instead of passing the whole normal model as a property, the just take the bits and pieces they need from the normal model and place them into the view (ie: you might just need half the properties in the normal model to be in the view model). This is because, remember, the view model will be passed to the view and they don't wanna pass things they wont use :). But this means you are going to need to set each of those properties one by one most likely (unless those exact ones are encapsulated in a sub class which usually doesn't happen by chance lol).
I kept it in one so you can just copy the normal model over in one shot for simplicity.
Now when you pass the view model to your view (MyViewModel) you will be able to use and access the normal model through the object notation and it's properties, eg... Model.MyNormalModel.Property1. Etc and do whatever you want with it in the view... Also, you can access the rest of your view model (the const values that we set) like this... Model.MyPathConstant1 and Model.MyPathConstant2 etc... So you have access to practically everything you want, ur normal model and whatever else you added later on all through what is now called your view model.
Please excuse typos -writing from and ipad lol. Let me know if this is making more sense.
You could use the built-in property of UrlHelper Content:
#Url.Content("~/Content/Stylsheets")
#Url.Content("~/Content/Images")
I have an MVC web app that uses a custom folder & namespace for resource files. I need to be able to pull late-bound values from my resource file, i.e. using the resource type and string. I tried using:
HttpContext.GetGlobalResourceObject("ResourceClass", "SomeKey")
But that returns null. When I move the resource to App_GlobalResources, that works, but cascades into other issues. I tried working through those, but they seem to be deeper and more in number than going back to my original plan of just being able to read from the resource file in the custom folder.
In a nutshell, I'm trying to localize my xVal validation, both the error message, and in the case of a RegEx validator, the pattern. I've got everything working, except for this one piece, where I'm trying to localize the pattern. Since this isn't built into DataAnnotations.RegularExpressionAttribute, I need to fetch it myself, based on the resource type and name provided in the attribute. Hence my dilemma.
Can I get to the resource value using some other method? Or, must my resource file reside in the App_GlobalResources folder? If the latter, then I will need to open another discussion for all my other issues -- or implement some cruder form of localization for the regex stuff.
Thanks in advance.
Jerad
This is the solution we came up with:
public static class ResourceHelper
{
public static string GetString(Type resourceType, string resourceName)
{
return new ResourceManager(resourceType.FullName, resourceType.Assembly)
.GetString(resourceName);
}
public static string GetString(Type resourceType, string resourceName, CultureInfo culture)
{
return new ResourceManager(resourceType.FullName, resourceType.Assembly)
.GetString(resourceName, culture);
}
public static object GetObject(Type resourceType, string resourceName)
{
return new ResourceManager(resourceType.FullName, resourceType.Assembly)
.GetObject(resourceName);
}
public static object GetObject(Type resourceType, string resourceName, CultureInfo culture)
{
return new ResourceManager(resourceType.FullName, resourceType.Assembly)
.GetObject(resourceName, culture);
}
}
I'm using this to get the current page name..so this returns for example MyPage.aspx
public string GetCurrentPageName()
{
string urlPath = Request.Url.AbsolutePath;
FileInfo fileInfo = new FileInfo(urlPath);
string pageName = fileInfo.Name;
return pageName;
}
There has to be an easier way? Meaning there's got to be an existing method or property in the .NET framework one would think.
The way I interpret the question, what you're looking for is an efficient way of retrieving the name of the currently executing aspx page, i.e. System.Web.UI.Page.
If that is true you shouldn't have to deal with any FileInfo objects or hit the filesystem. Simply use the AppRelativeVirtualPath property on the page object:
using System;
using System.IO;
using System.Web.UI;
namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string pageName = Path.GetFileNameWithoutExtension(Page.AppRelativeVirtualPath);
}
}
}
If you want to get the fully-qualified (or "rooted") path of your currently executing page you can use Server.MapPath like this:
string path = Server.MapPath(Page.AppRelativeVirtualPath);
Using AppRelativeVirtualPath has the benefit of working even when you're using URL rewriting and, since it doesn't use Request.Url (which is provided by your users), you don't have to worry about potentially malicious data.
Would HttpContext.Current.CurrentHandler be what you're looking for?
Since you're more interested in the physical file name vs the page object something along the lines of
var page = (Page) HttpContext.Current.CurrentHandler;
string url = page.AppRelativeVirtualPath;
This along with the information from #Markus Olsson can give you access to the page in any point during its execution even if you're outside of page class.
I use Request.Url.Segments.Last() , which I think is elegant enough.
just for interest I did little search with intellisence. did not found any property. still same logic in other way round.
string currentPageName = System.IO.Path.GetFileName(Request.Url.AbsolutePath);
As was pointed out in one of the answers to this earlier question of yours, I'd go for an option that didn't need me to create a FileInfo object.
There isn't always a direct mapping between a requested page and a file system object, especially when routing/url rewriting etc comes in to play.
Dim MyPage As String = Path.GetFileName(Page.AppRelativeVirtualPath.ToString).ToString
This one works for me
Not much better, but you could try this extension method:
public static string GetPageName(this Page myPage)
{
FileInfo fi =new FileInfo(myPage.MapPath(myPage.AppRelativeVirtualPath));
return fi.Name;
}
and just call it in your page's "OnInit" or whatever method as:
string pageName = this.GetPageName();
Marc
System.IO.Path.GetFileName(Request.PhysicalPath)