Get thymeleaf html file with all includes - html

Hy guys, I'm new to Thymeleaf, my goal is to have an endpoint which returns a String with the content of a html file. Should be easy, but that file html contains thymeleaf code and uses fragments from others files so using Files.readString(path) does not reach the goal.
How can I include them (I want to only include fragments, I don't have to process the file)?
That's what I've done till now:
#GetMapping(path = "/get-template-html")
public String getTemplateHTMLEndpoint() {
String templateHtml = "Problems reading template.html";
try {
String stringPath = new ClassPathResource("templates/template.html").getFile().getPath();
Path path = Path.of(stringPath);
templateHtml = Files.readString(path);
} catch (IOException e) {
e.printStackTrace();
}
return templateHtml;
}
But in another method where I process the file passing the context I got an error about the fragment "footer::footer"
String templateHtml = callGetTemplateHTMLEndPoint();
StringTemplateResolver stringTemplateResolver = new StringTemplateResolver();
stringTemplateResolver.setTemplateMode(TemplateMode.HTML);
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(stringTemplateResolver);
Context context = new Context();
context.setVariable("expenseReportPdf", expenseReportPdf);
context.setVariable("expensePdf", expensePdfList);
context.setVariable("expenseImg", jpgFile);
context.setVariable("amountCompanyCurrency", amountCompanyCurrency);
context.setVariable("expenseIncurredList", expenseIncurredList);
context.setVariable("expenseRiepiloghiList",expenseRiepiloghiList);
context.setVariable("advancePayBigDecimal", advancePayBigDecimal);
context.setVariable("dailyAllowanceList", dailyAllowanceList);
context.setVariable("dailyAllowanceFlag", dailyAllowanceFlag);
context.setVariable("logo",logo);
context.setVariable("logoSmartex",logoSmartex);
String renderedHtmlContent = templateEngine.process(templateHtml, context);

If you created a "standard" Spring Boot with Thymeleaf project (e.g. via https://start.spring.io), then the templates are automatically resolved if you put them in src/main/resources/templates
So if you created src/main/resources/templates/templates.html, then you can create a controller that will use that template like this:
#Controller
#RequestMapping("/")
public class MyController {
#GetMapping("/get-template-html")
public String getTemplateHtml() {
return "templates"; // name of your template file without the .html extension
}
}
If you start the application and access http://localhost:8080/get-template-html, you should see the HTML of the template.

Related

Select JSON sub-node

I am querying the Wikipedia API and am getting JSON back that looks like this:
https://en.wikipedia.org/w/api.php?action=query&prop=pageimages&titles=cessna%20172&pithumbsize=500&format=json
{"batchcomplete":"","query":{"normalized":[{"from":"cessna 172","to":"Cessna 172"}],"pages":{"173462":{"pageid":173462,"ns":0,"title":"Cessna 172","thumbnail":{"source":"https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Cessna_172S_Skyhawk_SP%2C_Private_JP6817606.jpg/500px-Cessna_172S_Skyhawk_SP%2C_Private_JP6817606.jpg","width":500,"height":333},"pageimage":"Cessna_172S_Skyhawk_SP,_Private_JP6817606.jpg"}}}}
Using .Net Core 2.2, what is the proper way to get the image thumbnail out of this (the source property in this case)?
Parsing JSON is not a built in feature in .Net core 2.2 so you will want to add the Newtonsoft.Json package to the project with dotnet add package Newtonsoft.Json --version 12.0.3.
From there include Newtonsoft.Json by adding using Newtonsoft.Json.Linq; to the top of the file. and using System.Net; to use WebClient.
From there the code retrieves the string from the url. JObject.Parse parses the string as a JObject. We can get the property you want by chaining indexers: ["query"]["pages"]["173462"]["thumbnail"]["source"].
Full source:
using System;
using System.Net;
using Newtonsoft.Json.Linq;
class Program
{
static void Main(string[] args)
{
const string url = "https://en.wikipedia.org/w/api.php?action=query&prop=pageimages&titles=cessna%20172&pithumbsize=500&format=json";
using (WebClient client = new WebClient())
{
string rawString = client.DownloadString(url);
var jsonResult = JObject.Parse(rawString);
string thumbnail = jsonResult["query"]["pages"]["173462"]["thumbnail"]["source"];
Console.WriteLine(thumbnail);
}
}
}
Ideally, you will have to define a class and de-serialised the json. Example :
Batch batch = JsonConvert.DeserializeObject<Account>(json);
More details here.
However, at times, just to get one/two values, it might be overhead to use an entire class structure. In this case, a quick way might be to parse the json dynamically. Example which is taken from here:
public void JValueParsingTest()
{
var jsonString = #"{""Name"":""Rick"",""Company"":""West Wind"",
""Entered"":""2012-03-16T00:03:33.245-10:00""}";
dynamic json = JValue.Parse(jsonString);
// values require casting
string name = json.Name;
string company = json.Company;
DateTime entered = json.Entered;
Assert.AreEqual(name, "Rick");
Assert.AreEqual(company, "West Wind");
}

Is it possible to get raw HTML from Razor/Blazor components?

I'd like to set up a "mailer/newsletter" using MailKit. My site stack is based off of Blazor web assembly and uses .Razor components.
I'm wondering if there is a way to consume a razor component I've written to output HTML into the MimeMessage object I'm using to generate my email body and what that architecture would look like / the best way to accomplish this?
Similar question (though not Blazor):
Can I use an ASP.Net MVC Razor view to generate an nicely formatted HTML Body as the input for an email sent from the server?
Late answer since I just saw this question: I wrote an alternative system called BlazorTemplater which uses .razor files instead of .cshtml since I had exactly this problem.
You can convert your templates to .razor format and then use BlazorTemplater to render to HTML:
var html = new ComponentRenderer<MyRazorClass>()
.Set(c => c.SomeParameter = someValue)
.Render();
It supports parameters, DI injection and nested components so you should find it useful! It's also much easier to set up and works in Razor Class Libraries too.
I am using Blazor with MailKit here: Google Email Viewer in Server Side Blazor
I use MarkupString to display the email content like this:
#using MimeKit
#using MessageReader
#strError
<div style="padding:2px; vertical-align:top">
<div><i>#MimeKitMessage.Date.ToString()</i></div>
<div><b>From:</b> #MimeKitMessage.From.ToString()</div>
<div><b>To:</b> #MimeKitMessage.To.ToString()</div>
<div><b>Subject:</b> #MimeKitMessage.Subject</div>
<br />
<div>#((MarkupString)#htmlEmail)</div>
</div>
#code {
[Parameter] public Message paramMessage { get; set; }
MimeMessage MimeKitMessage;
string strError = "";
string htmlEmail = "";
protected override void OnInitialized()
{
try
{
if (paramMessage != null)
{
string converted = paramMessage.Raw.Replace('-', '+');
converted = converted.Replace('_', '/');
byte[] decodedByte = Convert.FromBase64String(converted);
using (Stream stream = new MemoryStream(decodedByte))
{
// Convert to MimeKit from GMail
// Load a MimeMessage from a stream
MimeKitMessage = MimeMessage.Load(stream);
// Convert any embedded images
var visitor = new HtmlPreviewVisitor();
MimeKitMessage.Accept(visitor);
htmlEmail = visitor.HtmlBody;
//If the email has attachments we can get them here
//var attachments = visitor.Attachments;
}
}
}
catch (Exception ex)
{
strError = ex.Message;
}
}
}

.NET Core 2.1 - Accessing Config/usermanager in a static helper

I've recently moved from MVC5 over to .NET Core 2.1 (MVC). Can anyone help me with this please.
I have my ApplicationUser and I've extended the model/table to store the user's FirstName.
In the View, I want to be able to output the current user firstname value.
User in the view is a ClaimsPrincipal so I need to go off to the DB to grab the value I need or access UserManager to get it.
Now, I know I can get that in the controller but I don't want to have to create a JQuery call to grab it every time I need it.
What I do want is to be able to access it server side, ideally via a static helper class.
In the MVC5 I'd have a helper to do the job no problem. Something like this for example:
public static string GetCurrentUserFirstName()
{
string _usrRef = HttpContext.Current.User.Identity.GetUserId();
var user = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(_usrRef);
return user.FirstName;
}
However, .NET Core doesn't work that way.
In a controller I could say:
var user = await _userManager.GetUserAsync(User);
string firstName = user.FirstName;
or I could go off to the DB via a call using Dapper w/ my connection string.
I can't inject the UserManager or ConnectionStrings into the helper via the constructor as it is static.
Is there a way to access either of those in this static helper?
It's the little changes that get you the most!
Thanks to #Kirk Larkin - I've found the solution.
I have to admit, it feels a little more convoluted having to pass things around to gain access to them but this is a good, working solution.
The View:
#using Microsoft.AspNetCore.Identity
#using MyApplication.Helpers
#inject UserManager<ApplicationUser> UserManager
<div>
#await MyHelper.GetLoggedInUserFirstName(UserManager, User)
</div>
The MyHelper file:
public static async Task<string> GetLoggedInUserFirstName(UserManager<ApplicationUser> userManager, ClaimsPrincipal user)
{
string output = "";
try
{
var currentUser = await userManager.GetUserAsync(user);
if(currentUser!=null)
{
output = currentUser.FirstName ?? currentUser.Email;
}
}
catch(Exception e) { }
return output;
}

"Invalid JSON Number" Spring Data Mongo 2.0.2

** FIXED **
All I had to do is add an apostrophe before and after each argument index,
i.e, change:
#Query(value = "{'type': 'Application','name': ?0,'organizationId': ?1}", fields = "{_id:1}")
To:
#Query(value = "{'type': 'Application','name': '?0','organizationId': '?1'}", fields = "{_id:1}")
===================
I recently upgraded my MongoDB and my Spring-Data-MongoDB Driver.
I used to access my MongoDB through mongoRepository using this code:
#Query(value = "{'type': 'Application','name': ?0,'organizationId': ?1}", fields = "{_id:1}")
Policies findPolicyByNameAndOrganizationId(String name, String organizationId);
Where Policies is the object I want to consume.
After performing an update to Spring, I get the following Error now when accessing the method above:
org.bson.json.JsonParseException: Invalid JSON number
I fear this is because I use Spring's MongoCoverter (in the case of this specific object only) to map documents to object.
Here's is my Reader Converter:
public class ApplicationPolicyReadConverotor implements Converter<Document, ApplicationPolicy > {
private MongoConverter mongoConverter;
public ApplicationPolicyReadConverotor(MongoConverter mongoConverter) {
this.mongoConverter = mongoConverter;
}
//#Override
public ApplicationPolicy convert(Document source) {
ApplicationPolicyEntity entity = mongoConverter.read(ApplicationPolicyEntity.class, source);
ApplicationPolicy policy = new ApplicationPolicy();
addFields(policy, entity);
addPackages(policy, entity);
return policy;
}
And here's is my Writer Converter:
public class ApplicationPolicyWriteConvertor implements Converter<ApplicationPolicy, Document>{
private MongoConverter mongoConverter;
public ApplicationPolicyWriteConvertor(MongoConverter mongoConverter) {
this.mongoConverter = mongoConverter;
}
#Override
public Document convert(ApplicationPolicy source) {
System.out.println("mashuWrite");
ApplicationPolicyEntity target = new ApplicationPolicyEntity();
copyFields(source, target);
copyPackages(source, target);
Document Doc = new Document();
mongoConverter.write(target, Doc);
return Doc;
}
I checked Spring reference (2.0.2) regarding MongoConverter and how it works and at this stage I think I'm doing it correctly.
Other object who do not use mapping/conversions suffer no problems.
Same did this Object (ApplicationPolicy) untill I upgraded my mongo and my spring driver.
My mongodb is 3.4.10 and Spring data mongo driver is 2.0.2.
Here's the code that initializes the MappingMongoCoverter Object:
(Adds my custom Converters).
SimpleMongoDbFactory simpleMongoDbFactory = new SimpleMongoDbFactory(client, dbName);
DefaultDbRefResolver defaultDbRefResolver = new DefaultDbRefResolver(simpleMongoDbFactory);
MongoMappingContext mongoMappingContext = new MongoMappingContext();
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(defaultDbRefResolver,
mongoMappingContext);
mappingMongoConverter.setMapKeyDotReplacement("_dot_");
// Adding custom read and write converters for permission policy.
mappingMongoConverter.setCustomConversions(new MongoCustomConversions(Arrays.asList(
new ApplicationPolicyWriteConvertor(mappingMongoConverter), new ApplicationPolicyReadConverotor(
mappingMongoConverter))));
mappingMongoConverter.afterPropertiesSet();
final MongoTemplate template = new MongoTemplate(simpleMongoDbFactory, mappingMongoConverter);
return template;
I know for sure that ReaderConverter WORKS legit (at least in some cases) since other aspects of the software use the custom ReaderConverter I've written and it works as expected.
Also when using debug mode (Intellij) I do not reach to the conversion code block when invoking the following:
#Query(value = "{'type': 'Application','name': ?0,'organizationId': ?1}", fields = "{_id:1}")
Policies findPolicyByNameAndOrganizationId(String name, String organizationId);
So basically I'm kinda clueless. I have a sense my converter Implementation is messy but couldn't fix it..

Strategy for accessing an application-wide setting on the client in a .NET Core web app

We are in the process of re-writing one of our applications using ASP.NET Core. The architecture we're trying for has a Web API running on a different URL from the presentation. The root URL for this API will change in different environments, of course, so I'm trying to figure out how I can set up configuration and access to the Web API root URL in the JavaScript that requires it for retrieving data. For example, say I have an AJAX call to fetch some data from the API:
$.ajax({
dataType: "json",
url: "http://this.url.will.change/api/whatever", //this will change!
success: function(response) {
//load the items
}
});
I've set up appsettings.json files for various build/deploy scenarios and have them reading and injecting nicely, so I can store the URL there.
{
"Data": {
"DefaultConnection": {
"ConnectionString": "whatever"
}
},
"AppSettings": {
"ApiRootUrl": "http://apiroot/api/"
}
}
I considered writing a UrlHelper extension to provide the Web API root, but I don't think there's a way to inject the IOptions object into a static extension method. So, my question is really this: How can I make a configuration setting globally available in my CSHTML and JavaScript?
Update your Startup.cs like below
public class Startup {
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
IConfigurationBuilder builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton(_ => Configuration);
}
}
Then on your controller you can inject configuration like this
public class ConfigurationController : Controller {
private readonly IConfigurationRoot config;
public ConfigurationController (IConfigurationRoot config) {
this.config = config;
}
public string Test() {
return config.Get<string>("AppSettings:ApiRootUrl");
}
}
We've used to create a special configuration controller which was responsible for creating a dynamic javascript file from selected configurations settings. You can inject IOptions to the controller. Then from the options you can construct a new custom configuration object which will hold only the properties you want to expose (you probably don't want to expose anything like connection string to your db).
Use a json library (like json.net) to serialize this custom configuration object to a JSON string and create file content out of it like
string fileContent = "var globalConf =" + JsonConvert.SerializeObject(configObject);
Convert the string to array of bytes and return it as FileContentResult.
We were also setting some cache headers so the browser didn't hit the controller each time and used cache.
Of course you need to setup routing o the call to specific URL will hit your controller and return the javascript file you have dynamically created. You can reference it on a website using usual script tag.
As for the server side rendering you can always include IOptions in the model (or create a new model which will wrap both options and the original model)