Can I get the final HTML output of a route in ASP.NET MVC3? - asp.net-mvc-3

Is there a way to get the "final HTML output" (what you get when you do "View source" in a browser) from code in ASP.NET MVC3 without making use of something like a WebRequest?
For example something like this:
string htmlCode = Url.GetHtml("Action", "Controller", new { id = 7 });

You can do it like this with RazorGenerator.Mvc which is available as a Nuget package. You can read more about it here: http://razorgenerator.codeplex.com/ It was intended for Unit testing but I think it is what your looking for.
var view = new myview();
HtmlDocument document = view.RenderAsHtml();
Hope that helps you in what your trying to do.

This works for me:
public static string ViewToString(string viewName, object model)
{
ViewData.Model = model;
using (var stringWriter = new StringWriter())
{
var view = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext = new ViewContext(ControllerContext, view.View, ViewData, TempData, stringWriter);
view.View.Render(viewContext, stringWriter);
view.ViewEngine.ReleaseView(ControllerContext, view.View);
return stringWriter.GetStringBuilder().ToString();
}
}

Related

How do you read POST data in an ASP.Net MVC 3 Web API 2.1 controller?

This does not seem to be as easy as I thought. I found some solutions on the web, but they are not working for me. I have an ASP.Net MVC 3 project with the Microsoft ASP.Net Web API 2.1 nuget package installed. Now, I want to be able to read data posted to a web api controller. The data sent will vary, so I cannot used a strongly typed ViewModel.
Here are the solutions I tried:
public void Post([FromBody]string value)
{
...
}
public void Post([FromBody]List<string> values)
{
...
}
public void Post([FromBody]NameValueCollection values)
{
...
}
But my value or values variables are always empty. I know the controller is receiving data however because I can check it by accessing (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"].Request.Form. It does not look like the proper way to retrieve the data though. There ought to be a cleaner way.
UPDATE:
Here is how I am posting the information:
I am posting the data from another controller in the same web application:
public ActionResult SendEmailUsingService()
{
dynamic email = new ExpandoObject();
email.ViewName = "EmailTest";
email.From = "fromaddress#yahoo.com";
email.To = "toaddress#gmail.com";
email.Fullname = "John Smith";
email.Url = "www.mysite.com";
IDictionary<string, object> data = email;
using (var wb = new WebClient())
{
string url = BaseUrlNoTrailingSlash + Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Emailer" });
var response = wb.UploadValues(url, "POST", data.ToNameValueCollection());
}
return View();
}
And here is what I am getting in my Post web api controller if I declare an httpContext variable like this:
var httpContext = (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"];
httpContext.Request.Form =
{ViewName=EmailTest&From=fromaddress%40yahoo.com&To=toaddress%40gmail.com&Fullname=John+Smith&Url=www.mysite.com}
httpContext.Request.Form is a System.Collections.Specialized.NameValueCollection {System.Web.HttpValueCollection}
I finally found the answer to my question here:
Web API Form Data Collection
The solution is to use FormDataCollection:
public void Post([FromBody]FormDataCollection formData)
{
...
}

Instantiate new System.Web.Http.OData.Query.ODataQueryOptions in nunit test of ASP.NET Web API controller

I have an ASP.NET MVC4 Web API project with an ApiController-inheriting controller that accepts an ODataQueryOptions parameter as one of its inputs.
I am using NUnit and Moq to test the project, which allow me to setup canned responses from the relevant repository methods used by the ApiController. This works, as in:
[TestFixture]
public class ProjectControllerTests
{
[Test]
public async Task GetById()
{
var repo = new Mock<IManagementQuery>();
repo.Setup(a => a.GetProjectById(2)).Returns(Task.FromResult<Project>(new Project()
{
ProjectID = 2, ProjectName = "Test project", ProjectClient = 3
}));
var controller = new ProjectController(repo.Object);
var response = await controller.Get(2);
Assert.AreEqual(response.id, 2);
Assert.AreEqual(response.name, "Test project");
Assert.AreEqual(response.clientId, 3);
}
}
The challenge I have is that, to use this pattern, I need to pass in the relevant querystring parameters to the controller as well as the repository (this was actually my intent). However, in the case of ODataQueryOptions-accepting ApiController methods, even in the cases where I would like to use just the default parameters for ODataQueryOptions, I need to know how to instantiate one. This gets tricky:
ODataQueryOptions does not implement an interface, so I can't mock it directly.
The constructor requires an implementation of System.Web.Http.OData.ODataQueryContext, which requires an implementation of something implementing Microsoft.Data.Edm.IEdmModel, for which the documentation is scarce and Visual Studio 2012 Find References and View Call Hierarchy do not provide insight (what implements that interface?).
What do I need to do/Is there a better way of doing this?
Thanks.
Looks like someone else already answered this in the comments here, but it's not a complete solution for my use-case (see comment below):
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Customer>("Customers");
var opts = new ODataQueryOptions<Customer>(new ODataQueryContext(modelBuilder.GetEdmModel(),typeof(Customer)), request);
This is the solution I have been using in my NUnit tests to inject ODataQueryOptions
private static IEdmModel _model;
private static IEdmModel Model
{
get
{
if (_model == null)
{
var builder = new ODataConventionModelBuilder();
var baseType = typeof(MyDbContext);
var sets = baseType.GetProperties().Where(c => c.PropertyType.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>));
var entitySetMethod = builder.GetType().GetMethod("EntitySet");
foreach (var set in sets)
{
var genericMethod = entitySetMethod.MakeGenericMethod(set.PropertyType.GetGenericArguments());
genericMethod.Invoke(builder, new object[] { set.Name });
}
_model = builder.GetEdmModel();
}
return _model;
}
}
public static ODataQueryOptions<T> QueryOptions<T>(string query = null)
{
query = query ?? "";
var url = "http://localhost/Test?" + query;
var request = new HttpRequestMessage(HttpMethod.Get, url);
return new ODataQueryOptions<T>(new ODataQueryContext(Model, typeof(T)), request);
}

ASP.NET MVC3 Dropdownlist never selects value in Edit view

I have a function that fills creates dropdownlist in ASP.NET MVC.
public static MvcHtmlString countryDropDown(this HtmlHelper helper, string name, string optionLabel, object selectedValue)
{
XmlDocument doc = new XmlDocument();
doc.Load(HttpContext.Current.Server.MapPath("~/App_Data/countries.xml"));
StringBuilder b = new StringBuilder();
b.Append(string.Format("<select name=\"{0}\" id=\"{0}\">", name));
if (!string.IsNullOrEmpty(optionLabel))
b.Append(string.Format("<option value=\"\">{0}</option>", optionLabel));
foreach (XmlNode node in doc.SelectNodes("//country"))
{
string selected = string.Empty;
if (node.Attributes["name"].Value == selectedValue as string)
{
selected = "selected=\"selected\"";
}
b.Append(string.Format("<option value=\"{0}\" {2}>{1}</option>", node.Attributes["name"].Value, node.Attributes["name"].Value, selected));
}
b.Append("</select>");
return MvcHtmlString.Create( b.ToString());
}
I use this function in Create and Edit views as:
#Html.countryDropDown("Country"," ", ViewData["Country"])
It shows list of countries perfectly but the problem is that I never selects the saved value in Edit page.
How the code can be modified so that it can select the value in edit page.
Solved using
#Html.countryDropDown("Country"," ", ViewData.Eval("Country"))
instead of
#Html.countryDropDown("Country"," ", ViewData["Country"]

Rendering a partial view to a string by calling an action method

I have been experimenting with rendering an view to a string using methods outlines here:
Render a view as a string
The issue is that I need to call my controller action which does not happen when calling View.Render.
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
viewResult.View.Render(viewContext, sw);
My question is, how can I call RenderAction on an arbitrary controller passing in a route? I am trying to composite together the results of a number of partial views into a single result which will get passed back to the browser.
My code so far. Works except that the action method is not called.
public static string RenderPartialViewToString(this Controller controller, string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = controller.ControllerContext.RouteData.GetRequiredString("action");
controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
var route = new RouteData();
route.Values.Add("controller", "Test1");
route.Values.Add("action", "Index");
var controller1 = new Test1Controller();
var controllerContext = new ControllerContext(new RequestContext(this.ControllerContext.HttpContext, route), controller1);
controller1.ControllerContext = controllerContext;
var viewString = controller1.RenderPartialViewToString("~/Views/Test1/Index.cshtml", (object)model);
My goal is to create a simple CMS system that composites together the results of a number of controller actions/views and outputs them into a layout.
I have a primary controller action that retrieves a page description from the database. The code loops over a list of other controllers and calls their actions which results in a dynamic object model and a list of partial html snippets that is handed off to a custom WebViewPage.
I'm somewhat unsure of what you're trying to accomplish. However, that aside RenderPartialViewToString is an extension method. To use within a controller action you could do something as simple as this:
var result = this.RenderPartialViewToString("Index", model)
Where model is the strongly typed model used within the "Index" view. If for example you were wanting to render a view to string to use within a JSON action you could return a JSONResult with:
return new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new {html = this.RenderPartialViewToString("Index", model)}
};

How do I get the full name of a user in .net MVC 3 intranet app?

I have an MVC 3 intranet application that performs windows authentication against a particular domain. I would like to render the current user's name.
in the view,
#User.Identity.Name
is set to DOMAIN\Username, what I want is their full Firstname Lastname
You can do something like this:
using (var context = new PrincipalContext(ContextType.Domain))
{
var principal = UserPrincipal.FindByIdentity(context, User.Identity.Name);
var firstName = principal.GivenName;
var lastName = principal.Surname;
}
You'll need to add a reference to the System.DirectoryServices.AccountManagement assembly.
You can add a Razor helper like so:
#helper AccountName()
{
using (var context = new PrincipalContext(ContextType.Domain))
{
var principal = UserPrincipal.FindByIdentity(context, User.Identity.Name);
#principal.GivenName #principal.Surname
}
}
If you indend on doing this from the view, rather than the controller, you need to add an assembly reference to your web.config as well:
<add assembly="System.DirectoryServices.AccountManagement" />
Add that under configuration/system.web/assemblies.
Another option, without requiring a helper... You could just declare context and principal before you need to utilize these values, and then utilize it like a standard output...
#{ // anywhere before needed in cshtml file or view
var context = new PrincipalContext(ContextType.Domain);
var principal = UserPrincipal.FindByIdentity(context, User.Identity.Name);
}
Then anywhere within the document, just call each variable as needed:
#principal.GivenName // first name
#principal.Surname // last name
If you have many controllers then using #vcsjones approach might be painfull.
Therefore I'd suggest creating extension method for TIdentity.
public static string GetFullName(this IIdentity id)
{
if (id == null) return null;
using (var context = new PrincipalContext(ContextType.Domain))
{
var userPrincipal = UserPrincipal.FindByIdentity(context, id.Name);
return userPrincipal != null ? $"{userPrincipal.GivenName} {userPrincipal.Surname}" : null;
}
}
And then you can use it in your view:
<p>Hello, #User.Identity.GetFullName()!</p>
If you've upgraded to Identity 2 and are using claims, then this kind of info would be a claim. Try creating an extension method:
public static string GetFullName(this IIdentity id)
{
var claimsIdentity = id as ClaimsIdentity;
return claimsIdentity == null
? id.Name
: string.Format("{0} {1}",
claimsIdentity.FindFirst(ClaimTypes.GivenName).Value,
claimsIdentity.FindFirst(ClaimTypes.Surname).Value);
}
Then you can use it in the view like this:
#Html.ActionLink("Hello " + User.Identity.GetFullName() + "!", "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" })
Add the below in your _ViewImports.cshtml page :
#using System.DirectoryServices.AccountManagement
Then, in your _Layouts.cshtml place the below :
#{
var context = new PrincipalContext(ContextType.Domain);
var principal = UserPrincipal.FindByIdentity(context, User.Identity.Name);}
Note: You can concatenate by creating additional variable ex:
var userName = #principal.Givenname + ", " + #principal.Surname;
You can not call the variable 'userName' directly, however, you can call 'userName' anywhere on the page by creating a hidden field.

Resources