How to add break line in string.Format - c#

I was trying to add a break line in string.Format. However it didn't work no matter I added \r\n or Environment.NewLine. Can anyone advise what is the proper way to add a break line into the statement? Thanks.
public async Task<IViewComponentResult> InvokeAsync()
{
var claimsIdentity = (ClaimsIdentity) User.Identity;
var claim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
var userFromDb = await _db.ApplicationUser.FirstOrDefaultAsync(u => u.Id == claim.Value);
var role = await _userManager.GetRolesAsync(userFromDb);
string r = string.Format("{0} \r\n ({1})", userFromDb.Name, role[0]);
return View("Default",r);
}

Your controller (or view component) shouldn’t actually be responsible for deciding how something is displayed. Whether you need for example a line break, a <br /> tag, or separate <p> tags is the responsibility of the view. So you should not attempt to solve this in the controller but instead pass the view just everything necessary to take care of the visual representation itself.
You can do that by create a custom view model that contains the values you need to pass the view. You then create this view model and set its properties and pass on the object to the view:
public async Task<IViewComponentResult> InvokeAsync()
{
var claimsIdentity = (ClaimsIdentity) User.Identity;
var claim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
var userFromDb = await _db.ApplicationUser.FirstOrDefaultAsync(u => u.Id == claim.Value);
var role = await _userManager.GetRolesAsync(userFromDb);
// pass a complex object with separate properties to the view instead
return View("Default", new MyViewComponentModel
{
Name = userFromDb.Name,
Role = role[0],
});
}
public class MyViewComponentModel
{
public string Name { get; set; }
public string Role { get; set; }
}
Then, in the view, specify this MyViewComponentModel as the view model type and access the properties to render the values properly:
#model MyViewComponentModel
<div>
<strong>#Model.Name</strong><br />
#Model.Role
</div>
This approach has another huge benefit over creating the HTML code in the controller code: If you were to pass HTML to the view, then the view would have to render that text as raw HTML. This means that the code is taken as it is and no HTML encoding happens with the content. This may work with save values that are coming from your database but can get dangerous very quickly as soon as there is some user-defined content in there. Then, you are basically allowing malicious users to decide what kind of HTML gets rendered on your website which is a very common security issue. So it’s best to avoid that and have as little raw HTML in your views as absolutely necessary.

You are adding the line-break and sending the string to an HTML view. HTML is white space agnostic. Line-breaks are white space. Therefore the line-break has zero effect on the displayed string.
To place a line-break in HTML use the <br> tag. Or, better still send the string as two strings and let the view decide how it is displayed.

\r\n this work in C# code
string Name = "Alex";
var role = "manager";
string newString = string.Format("{0} \r\n ({1})", Name, role);
Console.WriteLine(newString);
however if you want to return view so it must be html break line <br />
public static class BreakLine
{
public const string LineBreak = #"<br />";
}
string htmlString = string.Format(#"{0} {1} ({2})", Name, BreakLine.LineBreak, role);
Console.WriteLine(htmlString);

Related

How to implement asp-append-version="true" to background-image property?

I am trying to implement the HTMLTagHelper asp-append-version="true" to my images.
The problem is as regards the DOM distribution, I am not assigning the attribute to an <img> tag but to to a <div> containing the image with the background-url property.
Moreover, the div is generated before all the DOM is loaded and I don't know if there would be a different approach of doing it.
One is obvious, change the div to an img tag, but I don't want it as my design has to remain the same.
My javascript has hitherto been like this:
cardHTML += '<div asp-append-version="true" class="card littleCard" style="background-image: url(/Content/Img/Especialistas/LittleCard/' + especialista.idEspecialista + '.jpg' + ')' + '" >';
cardHTML += '</div>';
The asp-append-version="true" won't work on the div tag.
Any ideas on how to find an approach of dealing with this ?
Thanks
You can create a custom TagHelper to target all elements having an inline style attribute. The following example I've tried looks working fine but if you want something more standard (similar to ImageTagHelper, ...), you can try looking into the base class UrlResolutionTagHelper. I'm not so sure why it need to be more complicated in there in which basically you need to resolve the URL before actually processing it more. I've tried with a simple IFileVersionProvider and it works for relative paths as well (of course the resolved path should be at the current server's web root).
The following simple example works fine for attribute values of HtmlString (which is almost the usual case, some custom rendering may inject IHtmlContent that is not of HtmlString, for such complicated cases, you can refer to the source code for UrlResolutionTagHelper, even copying almost the exact relevant code there is fine):
//target only elements having an inline style attribute
[HtmlTargetElement(Attributes = "style")]
public class InlineStyleBackgroundElementTagHelper : TagHelper
{
readonly IFileVersionProvider _fileVersionProvider;
const string BACKGROUND_URL_PATTERN = "(background(?:-image)?\\s*:[^;]*url)(\\([^)]+\\))";
public InlineStyleBackgroundElementTagHelper(IFileVersionProvider fileVersionProvider)
{
_fileVersionProvider = fileVersionProvider;
}
//bind the asp-append-version property
[HtmlAttributeName("asp-append-version")]
public bool AppendsVersion { get; set; }
//inject ViewContext from the current request
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (AppendsVersion)
{
if (output.Attributes.TryGetAttribute("style", out var styleAttr))
{
//the value here should be an HtmlString, so this basically
//gets the raw plain string of the style attribute's value
var inlineStyle = styleAttr.Value.ToString();
var basePath = ViewContext.HttpContext.Request.PathBase;
inlineStyle = Regex.Replace(inlineStyle, BACKGROUND_URL_PATTERN, m =>
{
//extract the background url contained in the inline style
var backgroundUrl = m.Groups[2].Value.Trim('(', ')', ' ');
//append the version
var versionedUrl = _fileVersionProvider.AddFileVersionToPath(basePath, backgroundUrl);
//format back the inline style with the versioned url
return $"{m.Groups[1]}({versionedUrl})";
}, RegexOptions.Compiled | RegexOptions.IgnoreCase);
output.Attributes.SetAttribute("style", inlineStyle);
}
}
}
}
Usage: just like how you use the asp-append-version on other built-in tag helps. (like in your example).

How to get byte[] to display as a background image for a div on a view (C#, ASP.NET, MVC)

I am building a web app in C# and ASP.Net with an MVC framework. I have the app running on my local desktop (for now). The app has a SQL backend that stores all my data. I am able to pull the data from the SQL db successfully through a number of stored procedures. The data is able to successfully be transferred from the stored procedures all the way up to my view from the controller.
Part of the data being transferred to the view is a byte[] from an image stored in the db (datatype is VARBINARY(MAX)). In short, I am trying to get the data from this byte[] to display as a background image in a div. This div acts as a single image in a Bootstrap carousel.
Initially, I had the following as my controller:
public ActionResult Dashboard()
{
DashboardViewModelHolder holder = new DashboardViewModelHolder();
DiscoveryService discoveryService = new DiscoveryService();
holder.national_Elected_Officials = new List<National_Elected_Officials_Model>();
National_Elected_Officials_Model n = new National_Elected_Officials_Model();
foreach (List<object> official in discoveryService.retrieve_National_Elected_Officials())
{
for(int i = 0; i <= official.Count; i++)
{
int id = int.Parse(official.ElementAt(0).ToString());
string fname = official.ElementAt(1).ToString();
string lname = official.ElementAt(2).ToString();
byte[] pictureByteArray = (byte[])official.ElementAt(3);
string position = official.ElementAt(4).ToString();
string party = official.ElementAt(5).ToString();
string bio = official.ElementAt(6).ToString();
int yearsOfService = int.Parse(official.ElementAt(7).ToString());
int terms = int.Parse(official.ElementAt(8).ToString());
string branch = official.ElementAt(9).ToString();
Image picture = image_Adapter.byteArrayToImage(pictureByteArray);
n.ElectedOfficialID = id;
n.FirstName = fname;
n.LastName = lname;
n.Picture = picture;
n.Position = position;
n.Party = party;
n.Bio = bio;
n.YearsOfService = yearsOfService;
n.Terms = terms;
n.Branch = branch;
}
holder.national_Elected_Officials.Add(n);
}
return View(holder);
}
My thought process was that I would just call n.Picture in my view and it would render the picture. After several tries and tutorials later, I left n.Picture as a byte[] and processed it in its own ActionResult method as seen below:
public FileContentResult Image(byte[] pictureByteArray)
{
return new FileContentResult(pictureByteArray, "image/jpeg");
}
I call this in my view as the following:
<div class="fill" style="background-image:src(#Url.Action("Image", electedOfficial.Picture))"></div>
electedOfficial is a reference to the model being set in the controller (n.Picture).
Is there something that I am missing?
EDIT 1
I forgot to add that the div returns null when I debug and step through the code. This is because the line with the div never gets called on when debugging. If I have it set as Url.Action, the program will actually go to the controller before hitting the line. If I do Html.Action, the program will skip the line and go to the controller after. Both will return null as a result which returns an error on the controller side since nulls arent allowed.
Edit 2
I tried changing the div tag to the following:
<div class="fill" style="background-image:src(#{Html.Action("Image", electedOfficial.Picture);})"></div>
By putting the {} in the debugger actually parses the line as I step through. Now, the problem is that the controller is not receiving the value being passed to it from electedOfficial.Picture. Just to confirm, this variable does hold the correct value in the view.
If you have the full byte[] in your model, then you can put the data directly into the view:
<div style="background:url( data:image/jpeg;base64,#Convert.ToBase64String(electedOfficial.Picture) )"></div>
This will work without the need for a separate controller that returns a FileContentResult, but will be a longer initial page load since the user will download all of the images along with the page HTML.
If you want to use a Controller endpoint so the images can be referenced as a URL in the src attribute and downloaded after the HTML has rendered then you are not too far off. It would work better to have the controller accept ElectedOfficialID and return the FileContentResult from that.
public FileContentResult Image(int electedOfficialId)
{
byte[] picture = GetPicture(electedOfficialId);
return new FileContentResult(picture, "image/jpeg");
}
Simples way of doing that would be encoding image as base64 string and add new string property eg PictureAsString to model instead having Picture
controller
n.PictureAsString = Convert.ToBase64String(pictureByteArray)
view
<div style="background:url(data:image/jpeg;base64,#electedOfficial.PictureAsString )" ></div>
use handler(ASHX). and call handler url in src.
public class MyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//Get Id from somewhere
//Get binary data
context.Response.ContentType = "application/octet-stream";
context.Response.BinaryWrite(bytes);
}
}
You can convert your byte array into a picture using this way:
Convert your byte array into a base64 string.
Display it in <img> tag.
Here is the code:
#{
var base64 = Convert.ToBase64String(Model.ByteArray);
var imgSrc = String.Format("data:image/gif;base64,{0}", base64);
}
<img src="#imgSrc" />

HTML is being rendered as literal string using RazorEngine. How can I prevent this?

I'm trying to generate a HTML document with RazorEngine (http://razorengine.codeplex.com/). Everything is mostly working, but the problem I have now is some of the HTML is being rendered correctly, and HTML that I have nested in that is being rendered as literal HTML, so rather than the browser displaying the tables & divs as expected, it's displaying e.g.
"<table></table><div></div>"
I kick off this process by calling the following:
string completeHTML = RazorEngine.Razor.Parse("InstallationTemplate.cshtml", new { Data = viewModels });
The completeHTML is then written to a file.
"InstallationTemplate.cshtml" is defined as:
#{
var installationReport = new InstallationReport(Model.Data);
}
<!DOCTYPE html>
<html>
<head></head>
<body>
<div>
<!-- I would expect this to write the rendered HTML
in place of "#installationReport.DigiChannels()" -->
#installationReport.DigiChannels()
</div>
</body>
</html>
Where InstallationReport and DigiChannels are defined as follows:
public static class InstallationReportExtensions
{
public static string DigiChannels(this InstallationReport installationReport)
{
return installationReport.GetDigiChannelsHtml();
}
}
public class InstallationReport
{
public string GetDigiChannelsHtml()
{
// the following renders the template correctly
string renderedHtml = RazorReport.GetHtml("DigiChannels.cshtml", GetDigiChannelData());
return renderedHtml;
}
}
public static string GetHtml(string templateName, object data)
{
var templateString = GetTemplateString(templateName);
return RazorEngine.Razor.Parse(templateString, data);
}
After GetDigiChannelsHtml() runs and returns renderedHtml, the line of execution returns to TemplateBase.cs into the method ITemplate.Run(ExecuteContext context), which is defined as:
string ITemplate.Run(ExecuteContext context)
{
_context = context;
var builder = new StringBuilder();
using (var writer = new StringWriter(builder))
{
_context.CurrentWriter = writer;
Execute(); // this is where my stuff gets called
_context.CurrentWriter = null;
}
if (Layout != null)
{
// Get the layout template.
var layout = ResolveLayout(Layout);
// Push the current body instance onto the stack for later execution.
var body = new TemplateWriter(tw => tw.Write(builder.ToString()));
context.PushBody(body);
return layout.Run(context);
}
return builder.ToString();
}
When I inspect builder.ToString(), I can see it contains proper HTML for the InstallationTemplate.cshtml stuff, and escaped HTML for the DigiChannels.cshtml stuff. For example:
How can I get #installationReport.DigiChannels() to include the correct HTML instead of the escaped HTML that it's currently doing?
Have you tried:
#Raw(installationReport.DigiChannels())
Edit : I could use it in following way (MVC3)
#Html.Raw(installationReport.DigiChannels())
The alternative to #Raw is to change your API to return HtmlStrings in appropriate places
Represents an HTML-encoded string that should not be encoded again.
The default razor behaviour is to encode strings.

How to return multiple images from controller method?

I have an ASP.NET MVC controller that generates images (they are stored in memory, I don't want to store them on the hard drive) and should return them to my view.
The problem is: I don't how know how to return multiple images from one controller method (I want to show the images in 1 view). I know that I can return a single image with the FileResult for example, but I can't find out (not on google/stackoverflow) how to return multiple images from the same method. Splitting the method up in multiple methods can't be done. Oh, all of the images are converted to a byte[], but that can be reversed if necessary.
This should work. Note I am reading my images from disk for my example but they can come from memory or anywhere. Then on the client side use java-script to display them.
[HttpGet]
public JsonResult Images()
{
var image1Base64 = Convert.ToBase64String(System.IO.File.ReadAllBytes(Server.MapPath("~/Images/1.jpg")));
var image2Base64 = Convert.ToBase64String(System.IO.File.ReadAllBytes(Server.MapPath("~/Images/2.jpg")));
var jsonResult = Json(new { image1 = image1Base64, image2 = image2Base64 }, JsonRequestBehavior.AllowGet);
jsonResult.MaxJsonLength = int.MaxValue;
return jsonResult;
}
If you have access to the MIME type of the image, you could always render them as Base64-encoded images, instead of making another request to a different controller method. This is a view model I use:
public class ImageViewModel
{
public string FileName { get; set; }
public string MIME { get; set; }
public byte[] Data { get; set; }
public override string ToString()
{
return string.Format(#"data:{0};base64,{1}", MIME.ToLower(), Convert.ToBase64String(Data));
}
}
You can use the Filename property in the alt attribute of the <img /> tag, so the markup & model-binding in your view would look something like this (assuming Razor syntax):
<img src="#model.ToString()" alt="#model.FileName" />
You do lose image-caching, AFAIK - that hasn't been an issue for me, but is understandably a deal-breaker for some.
I think that you can solve it in another way, instead of returning multiple images, you can create a utility method that loads the image in the controller
public FileContentResult GetImage(int imageId)
{
var image = GetImageById(imageId); // get from a list for example
return File(image, "image/jpeg"); // your image Mime type
}
and in the View you can do the following, iterate over the images
#foreach (var image in Model)
{
<img alt="" src="#Url.Action("GetImage", "ControllerName", new {imageId =image.Id})"/>
}
You wish to show multiple images based on input of the user, but don't want to save to the hard disk (chat). Therefor I recommand you using a session variable to save the users input in. And the use a simple FileResult to return multiple images based on that session variable.
http://blog.theobjectguy.com/2009/12/session-with-style.html
If your requirement is to show multiple images in one view it would be easier to use 2 action methods. One that returns a image with FileResult and another where you just use standard html img tags pointing the first action method

How to display html elements like links in errors rendered via Html.ValidationSummary()

One of my error message renders a link. However, Html.ValidationSummary() encodes it and therefore it displays as follow:
An account with the mobile or email you have specified already exists.
If you have forgotten your password, please Reset it.
Instead, it should render as:
An account with the mobile or email you have specified already exists.
If you have forgotten your password, please Reset it.
The error is added to the ModelState inside view as follows:
if (...)
{
ViewData.ModelState.AddModelError(string.Empty, string.Format("An account with the mobile or email you have specified already exists. If you have forgotten your password, please {0} it.", Html.ActionLink("Reset", "Reset")));
}
In short, how should I prevent Html.ValidationSummarry() to selectively/entirely encoding html in errors.
The current HTML helpers for displaying error messages do not support this. However, you could write your own HTML helpers that display the error message without HTML escaping it, i.e. they would treat the error message as raw HTML.
As a starting point, you could use the ASP.NET MVC source code from Codeplex, specifically the ValidationSummary method of the ValidationExtensions class:
public static string ValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes) {
// Nothing to do if there aren't any errors
if (htmlHelper.ViewData.ModelState.IsValid) {
return null;
}
string messageSpan;
if (!String.IsNullOrEmpty(message)) {
TagBuilder spanTag = new TagBuilder("span");
spanTag.MergeAttributes(htmlAttributes);
spanTag.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
spanTag.SetInnerText(message);
messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
}
else {
messageSpan = null;
}
StringBuilder htmlSummary = new StringBuilder();
TagBuilder unorderedList = new TagBuilder("ul");
unorderedList.MergeAttributes(htmlAttributes);
unorderedList.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName);
foreach (ModelState modelState in htmlHelper.ViewData.ModelState.Values) {
foreach (ModelError modelError in modelState.Errors) {
string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);
if (!String.IsNullOrEmpty(errorText)) {
TagBuilder listItem = new TagBuilder("li");
listItem.SetInnerText(errorText);
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
}
}
}
unorderedList.InnerHtml = htmlSummary.ToString();
return messageSpan + unorderedList.ToString(TagRenderMode.Normal);
}
You can then change this method to treat the error message as raw HTML.
Two warnings though:
You're changing the meaning of certain properties of the ModelState class. While you get away with using your own HTML helpers now, a future version of ASP.NET MVC might introduce changes that no longer work with this approach.
Be very careful about not using error messages that aren't properly escaped so you don't expose your web app to XSS attacks. Certain standard validation annotation might not work any longer since they don't HTML escape the error message.

Categories