### Example CSP response header with nonce Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example HTTP response header after enabling CSP with nonce generation. The nonce value is generated per request. ```HTTP Content-Security-Policy: default-src https: self:; script-src 'nonce-4ze2IRazk4Yu/j5K6SEzjA'; ``` -------------------------------- ### Pebble template example (home.html) Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example Pebble template that renders a username if logged in, otherwise shows a not-logged-in message. Place this file at src/main/resources/views/home.html. ```html Home {% if loggedIn %}

username: {{ username }}

{% else %}

You are not logged in

{% endif %} ``` -------------------------------- ### Freemarker template: src/main/resources/views/home.ftl Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Freemarker template that renders the example from the Views section. Uses the `loggedIn` variable to conditionally display the username or a login prompt. ```freemarker Home <#if loggedIn??>

username: ${username}

<#else>

You are not logged in

``` -------------------------------- ### Renamed HTML output example Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example of the HTML output after renaming, showing the CSS classes rewritten to 'a-b-c' and 'a-b-c-d'. ```html
... content, and stuff...
``` -------------------------------- ### Login form HTML example Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html HTML login form that the fieldset annotations are meant to render. It posts username and password fields to /login. ```html
``` -------------------------------- ### TurboFrameView controller example in Java, Python, Kotlin, Groovy Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Controller route annotated with @TurboFrameView("form") and @View("edit") that returns a message object. The same example is shown in Java, Python, Kotlin, and Groovy. ```java @Produces(MediaType.TEXT_HTML) @TurboFrameView("form") @View("edit") @Get Map index() { return Map.of("message", new Message(1L, "My message title", "My message content")); } ``` ```python @Produces(MediaType.TEXT_HTML) @TurboFrameView("form") @View("edit") @Get def index(self) -> dict[str, object]: return {"message": Message(1, "My message title", "My message content")} ``` ```kotlin @Produces(MediaType.TEXT_HTML) @TurboFrameView("form") @View("edit") @Get fun index() = mapOf("message" to Message(1L, "My message title", "My message content")) ``` ```groovy @Produces(MediaType.TEXT_HTML) @TurboFrameView("form") @View("edit") @Get Map index() { ["message": new Message(id: 1L, name: "My message title", content: "My message content")] } ``` -------------------------------- ### Velocity template for home view Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example Velocity template that conditionally displays a username or a login prompt. ```velocity Home #if( $loggedIn )

username: $username

#else

You are not logged in

#end ``` -------------------------------- ### Controller rendering a template with the View annotation Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example of a controller that renders a template by passing a model as a java.util.Map via the returned response object. Place file-based views in src/main/resources/views or set micronaut.views.folder to use a different folder. ```java @Controller("/hello") public class HelloController { @View("hello") @Get public Map index() { return Map.of("name", "Jason"); } } ``` -------------------------------- ### Handlebars template for home view Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example Handlebars template that conditionally displays a username or a login prompt. ```handlebars Home {{#if loggedIn}}

username: {{username}}

{{else}}

You are not logged in

{{/if}} ``` -------------------------------- ### Thymeleaf template and layout files Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Thymeleaf template and layout files for the example shown in the Views section. The template uses th:replace to include the layout, and the layout defines a fragment with title and content parameters. ```HTML Home

username:

You are not logged in

``` ```HTML Layout Title

Layout H1

Layout content

Layout footer
``` -------------------------------- ### HTML form for book save Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example HTML form that the application should display. It posts to /books/save with fields for title and pages, including validation attributes (minlength, maxlength, min, max, required). ```html
``` -------------------------------- ### Configure webpack.client.js for Micronaut React SSR Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Webpack client configuration for a Micronaut React SSR setup. It bundles client.js and outputs to src/main/resources/views/static, defining SERVER as false and using Babel with React presets. ```javascript const path = require('path'); const webpack = require('webpack'); module.exports = { entry: ['./client.js'], devtool: false, output: { path: path.resolve(__dirname, '../resources/views/static'), filename: 'client.js', }, plugins: [ new webpack.DefinePlugin({ SERVER: false, }) ], module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env', '@babel/preset-react'] } } } ] } }; ``` -------------------------------- ### Render Turbo Stream with TurboStreamRenderer Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Renders a Turbo Stream with a server-side template using TurboStreamRenderer. Builds a TurboStream with an APPEND action, target DOM ID "dom_id", and a template view named "fruit" with a model containing a Fruit instance, then renders it to an Optional. The example is shown in Java, Python, Kotlin, and Groovy. ```Java String view = "fruit"; Map model = Collections.singletonMap("fruit", new Fruit("Banana", "Yellow")); TurboStream.Builder builder = TurboStream.builder() .action(TurboStreamAction.APPEND) .targetDomId("dom_id") .template(view, model); Optional writable = turboStreamRenderer.render(builder, null); ``` ```Python view = "fruit" model = {"fruit": Fruit("Banana", "Yellow")} builder = (TurboStream.builder() .action(TurboStreamAction.APPEND) .targetDomId("dom_id") .template(view, model)) writable = turbo_stream_renderer.render(builder, None) ``` ```Kotlin val view = "fruit" val model = mapOf("fruit" to Fruit("Banana", "Yellow")) val builder = TurboStream.builder() .action(TurboStreamAction.APPEND) .targetDomId("dom_id") .template(view, model) val writable = turboStreamRenderer.render(builder, null) ``` ```Groovy String view = "fruit" Map model = Collections.singletonMap("fruit", new Fruit("Banana", "Yellow")) TurboStream.Builder builder = TurboStream.builder() .action(TurboStreamAction.APPEND) .targetDomId("dom_id") .template(view, model) Optional writable = turboStreamRenderer.render(builder, null) ``` -------------------------------- ### Enable CSP with micronaut.views.csp.enabled=true Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Enable Micronaut Views CSP by setting micronaut.views.csp.enabled=true. Shown in all supported configuration formats. ```Properties micronaut.views.csp.enabled=true ``` ```Yaml micronaut: views: csp: enabled: true ``` ```Toml micronaut = {views = {csp = {enabled = true}}} ``` ```Groovy micronaut { views { csp { enabled = true } } } ``` ```Hocon { micronaut { views { csp { enabled = true } } } } ``` ```JSON { "micronaut": { "views": { "csp": { "enabled": true } } } } ``` -------------------------------- ### Return POJO to view with @View and @Get Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Controller method returning a POJO annotated with @Introspected to a view. The method is annotated with @View("fruits") and @Get, and returns a new Fruit instance. The same example is shown in Java, Python, Kotlin, and Groovy. ```Java @View("fruits") @Get public Fruit index() { return new Fruit("apple", "red"); } ``` ```Python @View("fruits") @Get def index(self) -> Fruit: return Fruit("apple", "red") ``` ```Kotlin @View("fruits") @Get fun index() = Fruit("apple", "red") ``` ```Groovy @View("fruits") @Get Fruit index() { return new Fruit("apple", "red") } ``` -------------------------------- ### GET /turbofruit Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Documentation for the GET /turbofruit endpoint that uses the @TurboStreamView annotation to render Turbo Streams. ```APIDOC ## [GET] /turbofruit ### Description Renders a Turbo Stream response by annotating a controller route with @TurboStreamView. The route returns a map containing a fruit object, which is rendered as a Turbo Stream template. ### Method GET ### Endpoint /turbofruit ### Parameters #### Query Parameters None #### Request Body None ### Request Example ``` HttpRequest request = HttpRequest.GET("/turbofruit") .accept(TurboMediaType.TURBO_STREAM, MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML) .header(TurboHttpHeaders.TURBO_FRAME, "dom_id"); ``` ### Response #### Success Response (200) - **Content-Type**: text/vnd.turbo-stream.html - **Body**: A Turbo Stream HTML fragment with action "append" and target "dom_id" #### Response Example ``` ""+ "" + "" ``` ``` -------------------------------- ### CustomSoyFileSetProvider implementation Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html A naive implementation of SoyFileSetProvider that loads Soy templates from the configured views folder. It uses the resource loader to find each template file and adds it to a SoyFileSet builder. ```java @Singleton public class CustomSoyFileSetProvider implements SoyFileSetProvider { private static final Logger LOG = LoggerFactory.getLogger(CustomSoyFileSetProvider.class); private static final String[] VIEWS = { "home.soy" }; private final String folder; private final ResourceLoader resourceLoader; public CustomSoyFileSetProvider(ViewsConfiguration viewsConfiguration, ResourceLoader resourceLoader) { this.folder = viewsConfiguration.getFolder(); this.resourceLoader = resourceLoader; } @Override public SoyFileSet provideSoyFileSet() { final SoyFileSet.Builder builder = SoyFileSet.builder(); for (final String template : VIEWS) { resourceLoader.getResource(folder + "/" + template).ifPresent(url -> { try { builder.add(new File(url.toURI())); } catch (URISyntaxException e) { if (LOG.isWarnEnabled()) { LOG.warn("URISyntaxException raised while generating the SoyFileSet for folder {}", folder, e); } } }); } return builder.build(); } } ``` ```python @Singleton class CustomSoyFileSetProvider(SoyFileSetProvider): VIEWS = ["home.soy"] def __init__(self, views_configuration: ViewsConfiguration, resource_loader: ResourceLoader): self.folder = views_configuration.getFolder() self.resource_loader = resource_loader def provideSoyFileSet(self) -> SoyFileSet: builder = SoyFileSet.builder() for template in self.VIEWS: url = self.resource_loader.getResource(self.folder + "/" + template) if url.isPresent(): try: builder.add(File(url.get().toURI())) except Exception: LOG.warning("Exception raised while generating the SoyFileSet for folder %s", self.folder, exc_info=True) return builder.build() ``` -------------------------------- ### Create server.js for React SSR Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Server-side entry point for React SSR. Imports and re-exports React, ReactDOMServer, and page components. The bundle must be compatible with GraalJS, which is not NodeJS and doesn't support the same APIs. ```javascript import React from 'react'; import ReactDOMServer from 'react-dom/server'; // Page components import App from './components/App'; export { React, ReactDOMServer, App }; ``` -------------------------------- ### Scaffold a Micronaut project with views-react using mn CLI Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Use the Micronaut CLI to scaffold a new project with ReactJS views support. The --features=views-react flag enables the views-react feature, and the project name is my-cool-project. The build system (Gradle or Maven) is selected with the --build flag; Gradle is recommended for faster builds. ```bash $ mn create-app --features=views-react my-cool-project ``` -------------------------------- ### Minified CSS matching renamed classes Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Example of the minified CSS that matches the renamed classes, with rules for 'a-b-c' and 'a-b-c-d'. ```css .a-b-c{color:blue;}.a-b-c-d{background:yellow;} ``` -------------------------------- ### Generate renaming map with Closure Stylesheets Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Generate a JSON renaming map with Closure Stylesheets. The command outputs the map to `src/main/resources/renaming-map.json` and the minified CSS to `src/main/resources/styles/app-styles.min.css`. ```bash > java -jar closure-stylesheets.jar \ --output-renaming-map-format JSON \ --output-renaming-map src/main/resources/renaming-map.json \ --rename CLOSURE \ [...inputs...] > src/main/resources/styles/app-styles.min.css ``` -------------------------------- ### Create client.js for React hydration Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Client-side entry point that hydrates the React app after page load. Uses dynamic import to load the page component specified by Micronaut.rootComponent and hydrates the root with Micronaut.rootProps. ```javascript import React from 'react'; import {hydrateRoot} from 'react-dom/client'; const pageComponentName = Micronaut.rootComponent; import(`./components/${pageComponentName}.js`).then(module => { const PageComponent = module[pageComponentName] hydrateRoot(document, ) }) ``` -------------------------------- ### Render Turbo Streams with @TurboStreamView Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Annotate a controller route with @TurboStreamView to render Turbo Streams. The action is APPEND and the view is "fruit". The route is /turbofruit and returns a map with a Fruit object. ```Java @Produces(value = {MediaType.TEXT_HTML, TurboMediaType.TURBO_STREAM}) @TurboStreamView(value = "fruit", action = TurboStreamAction.APPEND) @Get("/turbofruit") Map show() { return Collections.singletonMap("fruit", new Fruit("Banana", "Yellow")); } ``` ```Python @Produces(value=[MediaType.TEXT_HTML, TurboMediaType.TURBO_STREAM]) @TurboStreamView(value="fruit", action=TurboStreamAction.APPEND) @Get("/turbofruit") def show(self) -> dict[str, object]: return {"fruit": Fruit("Banana", "Yellow")} ``` ```Kotlin @Produces(value = [MediaType.TEXT_HTML, TurboMediaType.TURBO_STREAM]) @TurboStreamView(value = "fruit", action = TurboStreamAction.APPEND) @Get("/turbofruit") fun show() = mapOf("fruit" to Fruit("Banana", "Yellow")) ``` ```Groovy @Produces(value = [MediaType.TEXT_HTML, TurboMediaType.TURBO_STREAM]) @TurboStreamView(value = "fruit", action = TurboStreamAction.APPEND) @Get("/turbofruit") Map show() { return Collections.singletonMap("fruit", new Fruit("Banana", "Yellow")); } ``` -------------------------------- ### Preact client.js for Micronaut React SSR Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Client-side hydration entry point for Preact. Dynamically imports the page component based on `Micronaut.rootComponent` and renders it with `Micronaut.rootProps` into the document. Used as `client.js` in the Micronaut React SSR setup. ```javascript import {h, render} from 'preact' const pageComponentName = Micronaut.rootComponent; import(`./components/${pageComponentName}.js`).then(module => { const PageComponent = module[pageComponentName] render(h(PageComponent, Micronaut.rootProps, null), document) }) ``` -------------------------------- ### Compile Soy templates with SoyToJbcSrcCompiler Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Compile Soy templates to Java bytecode. This step is required before providing the renaming map to Micronaut Views Soy. ```bash > java -jar SoyToJbcSrcCompiler.jar \ --output templates.jar \ --srcs [...templates...]; ``` -------------------------------- ### Generated Turbo Stream HTML output Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html The generated Turbo Stream HTML string from the previous example. The output is a turbo-stream element with action "append" and target "dom_id", containing the template content. ```Java "" + "" + "" ``` ```Python "" "" "" ``` ```Kotlin "" + "" + "" ``` ```Groovy '' + '' + '' ``` -------------------------------- ### Kotlin RewriteMapProvider implementing SoyNamingMapProvider Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Kotlin implementation of SoyNamingMapProvider that loads a JSON renaming map from a resource and provides a SoyCssRenamingMap. The map is loaded lazily and defaults to an empty map if the resource is missing. ```kotlin @Singleton class RewriteMapProvider(resourceLoader: ResourceLoader, jsonMapper: JsonMapper) : SoyNamingMapProvider { private val cssRenamingMap: Map = // load the renaming map embedded as a resource resourceLoader.getResourceAsStream(RENAMING_MAP_NAME).map { json -> json.use { jsonMapper.readValue(it, Argument.mapOf(String::class.java, String::class.java)) } }.orElse(emptyMap()) /** * Provide a CSS renaming map to Soy/Micronaut. * * @return Inflated Soy CSS renaming map. */ override fun cssRenamingMap(): SoyCssRenamingMap { // (or whatever logic you need to rewrite the class) return SoyCssRenamingMap { className -> cssRenamingMap[className] } } companion object { /** Filename for the JSON class renaming map. */ private const val RENAMING_MAP_NAME = "renaming-map.json" } } ``` -------------------------------- ### Bind HtmxRequestHeaders in controller method (Java, Python, Kotlin, Groovy) Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Bind HtmxRequestHeaders in a controller method. The parameter is bound when the request is an HTMX request; otherwise it is null. The example returns a different view depending on whether the header is present. ```java @Get ModelAndView> index(@Nullable HtmxRequestHeaders htmxRequestHeaders) { Map model = Collections.singletonMap("fruit", new Fruit("Apple", "Red")); if (htmxRequestHeaders != null) { return new ModelAndView<>("fruit", model); } return new ModelAndView<>("fruits", model); } ``` ```python @Get def index(self, htmxRequestHeaders: HtmxRequestHeaders | None) -> ModelAndView: model = {"fruit": Fruit("Apple", "Red")} if htmxRequestHeaders is not None: return ModelAndView("fruit", model) return ModelAndView("fruits", model) ``` ```kotlin @Get fun index(htmxRequestHeaders: HtmxRequestHeaders?): ModelAndView> { val model = mapOf("fruit" to Fruit("Apple", "Red")) if (htmxRequestHeaders != null) { return ModelAndView("fruit", model) } return ModelAndView("fruits", model) } ``` ```groovy @Get ModelAndView> index(@Nullable HtmxRequestHeaders htmxRequestHeaders) { Map model = [fruit: new Fruit("Apple", "Red")] if (htmxRequestHeaders != null) { return new ModelAndView<>("fruit", model) } new ModelAndView<>("fruits", model) } ``` -------------------------------- ### Preact server.js for Micronaut React SSR Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Server-side rendering entry point for Preact. Re-exports the `h` symbol from Preact and `renderToString` from the separate `preact-render-to-string` module, and exports each page component. Used as `server.js` in the Micronaut React SSR setup. ```javascript import {renderToString} from 'preact-render-to-string'; import * as preact from 'preact'; // Page components import App from './components/App'; export { preact, renderToString, App }; ``` -------------------------------- ### BookController with FormGenerator and validation error handling Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Controller that generates a form for creating a book, handles validation errors, and redirects to the list after saving. The Kotlin version uses `open class` and `companion object`; the Groovy version uses a plain class with static fields. Both define the same routes and error handling. ```kotlin @Controller("/books") open class BookController(private val formGenerator: FormGenerator, private val bookRepository: BookRepository) { @Produces(MediaType.TEXT_HTML) @View("/books/create.html") @Get("/create") fun create(): Map { return mapOf(FORM to formGenerator.generate(CONTROLLER_PATH + SAVE_PATH, BookSave::class.java)) } @Produces(MediaType.TEXT_HTML) @View("/books/list.html") @Get("/list") fun list(): Map { return mapOf("books" to bookRepository.findAll()) } @Produces(MediaType.TEXT_HTML) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Post(SAVE_PATH) open fun save(@Valid @Body bookSave: BookSave): HttpResponse<*> { bookRepository.save(Book(bookSave.title, bookSave.pages)) return HttpResponse.seeOther(UriBuilder.of(CONTROLLER_PATH).path("list").build()) } @Error(exception = ConstraintViolationException::class) fun onConstraintViolationException(request: HttpRequest<*>, ex: ConstraintViolationException): HttpResponse<*> { if (request.path == CONTROLLER_PATH + SAVE_PATH) { val bookSave = request.getBody(BookSave::class.java).orElse(null) if (bookSave != null) { val form = formGenerator.generate(CONTROLLER_PATH + SAVE_PATH, bookSave, ex) val body = ModelAndView("/books/create.html", mapOf(FORM to form)) return HttpResponse.unprocessableEntity().body(body) } } return HttpResponse.serverError() } companion object { const val CONTROLLER_PATH = "/books" const val SAVE_PATH = "/save" const val FORM = "form" } } ``` ```groovy @Controller("/books") class BookController { public static final String CONTROLLER_PATH = "/books" public static final String SAVE_PATH = "/save" public static final String FORM = "form" private final FormGenerator formGenerator private final BookRepository bookRepository BookController(FormGenerator formGenerator, BookRepository bookRepository) { this.formGenerator = formGenerator this.bookRepository = bookRepository } @Produces(MediaType.TEXT_HTML) @View("/books/create.html") @Get("/create") Map create() { [(FORM): formGenerator.generate(CONTROLLER_PATH + SAVE_PATH, BookSave)] } @Produces(MediaType.TEXT_HTML) @View("/books/list.html") @Get("/list") Map list() { [books: bookRepository.findAll()] } @Produces(MediaType.TEXT_HTML) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Post(SAVE_PATH) HttpResponse save(@Valid @Body BookSave bookSave) { bookRepository.save(new Book(bookSave.title, bookSave.pages)) HttpResponse.seeOther(UriBuilder.of(CONTROLLER_PATH).path("list").build()) } @Error(exception = ConstraintViolationException) HttpResponse onConstraintViolationException(HttpRequest request, ConstraintViolationException ex) { if (request.path == CONTROLLER_PATH + SAVE_PATH) { Optional bookSaveOptional = request.getBody(BookSave) if (bookSaveOptional.isPresent()) { Form form = formGenerator.generate(CONTROLLER_PATH + SAVE_PATH, bookSaveOptional.get(), ex) ModelAndView> body = new ModelAndView<>("/books/create.html", [(FORM): form]) return HttpResponse.unprocessableEntity().body(body) } } HttpResponse.serverError() } } ``` -------------------------------- ### Configure development file watching and cache disabling Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Development configuration to disable file caching and enable file watching for the views directory. The `paths` property assumes the default JS compilation setup from Micronaut Starter; adjust it if your JS bundle is elsewhere. This makes the server pick up rebuilt bundles without a restart, but does not trigger a browser refresh. ```properties micronaut.server.netty.responses.file.cache-seconds=0 micronaut.io.watch.enabled=true micronaut.io.watch.paths=build/resources/main/views ``` ```yaml micronaut: # For development purposes only. server: netty: responses: file: cache-seconds: 0 io: watch: enabled: true paths: build/resources/main/views ``` ```hocon micronaut = {server = {netty = {responses = {file = {cache-seconds = 0}}}}, io = {watch = {enabled = true, paths = "build/resources/main/views"}}} ``` ```groovy micronaut { server { netty { responses { file { cacheSeconds = 0 } } } } io { watch { enabled = true paths = "build/resources/main/views" } } } ``` ```json { micronaut { server { netty { responses { file { cache-seconds = 0 } } } } io { watch { enabled = true paths = "build/resources/main/views" } } } } ``` ```json { "micronaut": { "server": { "netty": { "responses": { "file": { "cache-seconds": 0 } } } }, "io": { "watch": { "enabled": true, "paths": "build/resources/main/views" } } } } ``` -------------------------------- ### Enable CSP nonce generation with generateNonce and {#nonceValue} Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Enable CSP nonce generation by setting generateNonce=true and include the {#nonceValue} token in policyDirectives. The token must be preceded by nonce- and wrapped in single quotes per CSP3. ```Properties micronaut.views.csp.enabled=true micronaut.views.csp.generateNonce=true micronaut.views.csp.policyDirectives=default-src https: self:; script-src 'nonce-{#nonceValue}'; ``` ```Yaml micronaut: views: csp: enabled: true generateNonce: true policyDirectives: "default-src https: self:; script-src 'nonce-{#nonceValue}';" ``` ```Toml micronaut = {views = {csp = {enabled = true, generateNonce = true, policyDirectives = "default-src https: self:; script-src \'nonce-{#nonceValue}\';"}}} ``` ```Groovy micronaut { views { csp { enabled = true generateNonce = true policyDirectives = "default-src https: self:; script-src 'nonce-{#nonceValue}';" } } } ``` ```Hocon { micronaut { views { csp { enabled = true generateNonce = true policyDirectives = "default-src https: self:; script-src 'nonce-{#nonceValue}';" } } } } ``` ```JSON { "micronaut": { "views": { "csp": { "enabled": true, "generateNonce": true, "policyDirectives": "default-src https: self:; script-src 'nonce-{#nonceValue}';" } } } } ``` -------------------------------- ### Integrate React Helmet head manager in a Micronaut Views render script Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Use this render script to integrate React Helmet as a head manager in a Micronaut Views server-side rendering setup. It creates the virtual DOM, renders the component to a string, extracts head data via Helmet.renderStatic(), and assembles the full HTML document with the head content and client bundle script. Remember to export Helmet from your server-side bundle. ```javascript export async function ssr(component, props, callback, config) { // Create the vdom. const element = React.createElement(component, props, null); // Render the given component, expecting it to fill a
in the tag. const body = ReactDOMServer.renderToString(element) // Get the data that should populate the from the Helmet library. const helmet = Helmet.renderStatic(); // Data to be passed to the browser after the main HTML has finished loading. const boot = { rootProps: props, rootComponent: component.name, }; // Assemble the HTML. const html = ` ${helmet.title.toString()} ${helmet.meta.toString()} ${helmet.link.toString()}
${body}
`; // Send it back. callback.write(html); } ``` -------------------------------- ### Implement SoyNamingMapProvider to provide CSS renaming map Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Provide the renaming map to Micronaut Views Soy by implementing `SoyNamingMapProvider`. The Java version loads the JSON map from a resource and returns a `SoyCssRenamingMap`; the Python version defines a `CssRenamingMap` class and a `RewriteMapProvider` singleton. ```java @Singleton public class RewriteMapProvider implements SoyNamingMapProvider { /** Filename for the JSON class renaming map. */ private static final String RENAMING_MAP_NAME = "renaming-map.json"; private final Map cssRenamingMap; RewriteMapProvider(ResourceLoader resourceLoader, JsonMapper jsonMapper) throws IOException { // load the renaming map embedded as a resource try (InputStream json = resourceLoader.getResourceAsStream(RENAMING_MAP_NAME).orElse(null)) { this.cssRenamingMap = json == null ? Collections.emptyMap() : jsonMapper.readValue(json, Argument.mapOf(String.class, String.class)); } } /** * Provide a CSS renaming map to Soy/Micronaut. * * @return Inflated Soy CSS renaming map. */ @Nullable @Override public SoyCssRenamingMap cssRenamingMap() { // (or whatever logic you need to rewrite the class) return cssRenamingMap::get; } } ``` ```python class CssRenamingMap(SoyCssRenamingMap): def __init__(self, renaming_map: dict[str, str]): self.renaming_map = renaming_map def get(self, className: str) -> str | None: # (or whatever logic you need to rewrite the class) return self.renaming_map.get(className) @Singleton class RewriteMapProvider(SoyNamingMapProvider): RENAMING_MAP_NAME = "renaming-map.json" # Filename for the JSON class renaming map. def __init__(self, resource_loader: ResourceLoader, json_mapper: JsonMapper): # load the renaming map embedded as a resource json = resource_loader.getResourceAsStream(self.RENAMING_MAP_NAME).orElse(None) self.css_renaming_map = {} if json is None else dict(json_mapper.readValue(json, Argument.mapOf(String, String))) def cssRenamingMap(self) -> SoyCssRenamingMap: return CssRenamingMap(self.css_renaming_map) ``` -------------------------------- ### Soy Controller in Python Source: https://micronaut-projects.github.io/micronaut-views/latest/guide/index.html Python controller that returns a dictionary of context parameters for the Soy template bound by the @View annotation. ```python @Controller("/soy") class SoyController: @View("sample.home") @Get def home(self) -> HttpResponse: return HttpResponse.ok({"loggedIn": True, "username": "sgammon"}) ```