I followed https://docs.jboss.org/author/display/WFLY10/Clustering+and+Domain+Setup+Walkthrough step by step.
Wildfly domain looks good.
I can read succesful registration log in both master and slave hosts.
That tutorial explains how to configure load balancing with mod_cluster. Anyway, I'd prefer takin' advantage of AWS Elastic Load Balancer service.
Then, I tried all three types of LB (application, network, classic), but none worked with me.
I wrote a simple app to test my use case:
Front servlet:
#WebServlet( urlPatterns = {"/test"} )
public class SessionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Inject SessionBean bean;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("doGet");
process(req, res, false);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("doPost");
process(req, res, true);
}
private void process(HttpServletRequest req, HttpServletResponse res, boolean post) throws ServletException, IOException {
if ( req.getParameter("name")!=null ) {
bean.setName( req.getParameter("name") );
}
req.setAttribute("name", bean.getName());
req.getRequestDispatcher( "/test.jsp" ).forward(req, res);
}
}
Session scoped bean:
#SessionScoped
public class SessionBean implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public SessionBean() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SessionBean other = (SessionBean) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
#Override
public String toString() {
return "SessionBean [name=" + name + "]";
}
}
View JSP:
<%# page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html lang="it">
<head>
<title>Session Failover Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="UTF-8" />
</head>
<body>
<h1>You are: <c:if test="${empty name}"><i>nobody</i></c:if> <font color="red">${name}</font></h1>
<br/>
<hr/>
<br/>
<form action="/session-failover-demo/test" method="post">
Who are you:
<input type="text" name="name" value="" />
<input type="submit" name="submit" value="ok" />
</form>
</body>
</html>
web.xml file has
<display-name>session-failover-demo</display-name>
<distributable/>
Finally, I make my test:
Browse the ELB endpoint /myapp/test
Set my name through the html form
Refresh a couple of time and see my name (from session)
Stop my 'master' ec-2 instance and wait a minute
The ELB notice master is down and start forwarding requests to 'slave' instance
Refresh the page and.... my name is... nobody!
The session is lost. I see in my browser dev tools that JSESSIONID cookie value has changed!
So I don't know whether I should blame Wildfly domain configuration or AWS ELB. Am I missing something?
EDIT Master stdout: https://pastebin.com/XbtVrb40
Related
I have a couple virtual machines running inside proxmox but I found out you can use the novnc client to connect to these virtual machines and use the console in a web browser. This is perfect for different clients I work with. I already have a small django project that shows the running nodes and vm's inside of them. Now I wanted to include de js library by novnc. However I can't really find a lot of information online and since my coding experience is not that high I don't really know what I am doing wrong.
I used this outdated repo and tried to see if I can find out how to get it to work. https://github.com/missuor/novnc-proxy-django
for now I have a view that call the api of proxmox. /api2/json/nodes/{node}/lxc/{vmid}/vncproxy to be precise. this returns a ticket, port, cert, user and upid. Now I am not sure if I just need to call the vncproxy or use these variable to open a websocket by /api2/json/nodes/{node}/lxc/{vmid}/vncwebsocket. The django view i currently have just call the vncproxy and sends the values to the rendered template.
This template I used from the repo metioned above, but I just don't know why I keep ketting a Server Disconnected(code 1006 error). If you have any tips for me that would be of great help.
this is the view I currently use which receives all the correct values.
def vnc(request, node, vmid):
vncproxy = proxmox_vnc().vncproxyVirtualMachine(node, vmid)
token = vncproxy['data']['ticket']
port = vncproxy['data']['port']
host = 'Settings.vnxproxy'
password = ''
print(token)
return render(request, 'vnc_auto.html', {'token': token, 'host': host, 'port': port, 'password': password})
And this below is the vnc.html I use to render and uses the novnc client.
{% load static %}
<!DOCTYPE html>
<html>
<head>
<!--
noVNC example: simple example using default UI
Copyright (C) 2012 Joel Martin
Copyright (C) 2013 Samuel Mannehed for Cendio AB
noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
Connect parameters are provided in query string:
http://example.com/?host=HOST&port=PORT&encrypt=1&true_color=1
-->
<title>noVNC</title>
<meta charset="utf-8">
<!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
Remove this if you use the .htaccess -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- Apple iOS Safari settings -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<!-- App Start Icon -->
<link rel="apple-touch-startup-image" href="{% static 'novnc/images/screen_320x460.png' %}" />
<!-- For iOS devices set the icon to use if user bookmarks app on their homescreen -->
<link rel="apple-touch-icon" href="{% static 'novnc/images/screen_57x57.png' %}">
<!--
<link rel="apple-touch-icon-precomposed" href="{% static 'novnc/images/screen_57x57.png' %}" />
-->
<script>var INCLUDE_URI='/static/novnc/include/'</script>
<!-- Stylesheets -->
<link rel="stylesheet" href="{% static 'novnc/include/base.css' %}" title="plain">
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="{% static 'novnc/include/util.js' %}"></script>
</head>
<body style="margin: 0px;">
<div id="noVNC_screen">
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
<table border=0 width="100%"><tr>
<td><div id="noVNC_status" style="position: relative; height: auto;">
Loading
</div></td>
<td width="1%"><div id="noVNC_buttons">
<input type=button value="Send CtrlAltDel"
id="sendCtrlAltDelButton">
<span id="noVNC_xvp_buttons">
<input type=button value="Shutdown"
id="xvpShutdownButton">
<input type=button value="Reboot"
id="xvpRebootButton">
<input type=button value="Reset"
id="xvpResetButton">
</span>
</div></td>
</tr></table>
</div>
<canvas id="noVNC_canvas" width="640px" height="20px">
Canvas not supported.
</canvas>
</div>
<script>
/*jslint white: false */
/*global window, $, Util, RFB, */
"use strict";
// Load supporting scripts
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
"jsunzip.js", "rfb.js", "keysym.js"]);
var rfb;
function passwordRequired(rfb) {
var msg;
msg = '<form onsubmit="return setPassword();"';
msg += ' style="margin-bottom: 0px">';
msg += 'Password Required: ';
msg += '<input type=password size=10 id="password_input" class="noVNC_status">';
msg += '<\/form>';
$D('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
$D('noVNC_status').innerHTML = msg;
}
function setPassword() {
rfb.sendPassword($D('password_input').value);
return false;
}
function sendCtrlAltDel() {
rfb.sendCtrlAltDel();
return false;
}
function xvpShutdown() {
rfb.xvpShutdown();
return false;
}
function xvpReboot() {
rfb.xvpReboot();
return false;
}
function xvpReset() {
rfb.xvpReset();
return false;
}
function updateState(rfb, state, oldstate, msg) {
var s, sb, cad, level;
s = $D('noVNC_status');
sb = $D('noVNC_status_bar');
cad = $D('sendCtrlAltDelButton');
switch (state) {
case 'failed': level = "error"; break;
case 'fatal': level = "error"; break;
case 'normal': level = "normal"; break;
case 'disconnected': level = "normal"; break;
case 'loaded': level = "normal"; break;
default: level = "warn"; break;
}
if (state === "normal") {
cad.disabled = false;
} else {
cad.disabled = true;
xvpInit(0);
}
if (typeof(msg) !== 'undefined') {
sb.setAttribute("class", "noVNC_status_" + level);
s.innerHTML = msg;
}
}
function xvpInit(ver) {
var xvpbuttons;
xvpbuttons = $D('noVNC_xvp_buttons');
if (ver >= 1) {
xvpbuttons.style.display = 'inline';
} else {
xvpbuttons.style.display = 'none';
}
}
window.onscriptsload = function () {
var host, port, password, path, token;
$D('sendCtrlAltDelButton').style.display = "inline";
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
$D('xvpShutdownButton').onclick = xvpShutdown;
$D('xvpRebootButton').onclick = xvpReboot;
$D('xvpResetButton').onclick = xvpReset;
WebUtil.init_logging(WebUtil.getQueryVar('logging', 'warn'));
document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
// By default, use the host and port of server that served this file
host = WebUtil.getQueryVar('host', '{{host}}'?'{{host}}':window.location.hostname);
port = WebUtil.getQueryVar('port', {{port}}?{{port}}:window.location.port);
// if port == 80 (or 443) then it won't be present and should be
// set manually
if (!port) {
if (window.location.protocol.substring(0,5) == 'https') {
port = 443;
}
else if (window.location.protocol.substring(0,4) == 'http') {
port = 80;
}
}
// If a token variable is passed in, set the parameter in a cookie.
// This is used by nova-novncproxy.
token = WebUtil.getQueryVar('token', null);
if (token) {
WebUtil.createCookie('token', token, 1)
}
password = WebUtil.getQueryVar('password', '{{password}}'?'{{password}}':'');
path = WebUtil.getQueryVar('path', '{{path}}'?'{{path}}':'');
path += '?token='+token;
if ((!host) || (!port)) {
updateState('failed',
"Must specify host and port in URL");
return;
}
rfb = new RFB({'target': $D('noVNC_canvas'),
'encrypt': WebUtil.getQueryVar('encrypt',
(window.location.protocol === "https:")),
'repeaterID': WebUtil.getQueryVar('repeaterID', ''),
'true_color': WebUtil.getQueryVar('true_color', true),
'local_cursor': WebUtil.getQueryVar('cursor', true),
'shared': WebUtil.getQueryVar('shared', true),
'view_only': WebUtil.getQueryVar('view_only', false),
'onUpdateState': updateState,
'onXvpInit': xvpInit,
'onPasswordRequired': passwordRequired});
console.log(host, port, password, path);
rfb.connect(host, port, password, path);
};
</script>
</body>
</html>
I am trying to display a subject name for a course where I saved the subject from a database driven dropdown list in a client-side Blazor app. The value returns as a Guid instead of the subject name. Has anyone accomplished this with Blazor? I couldn't find anything in the Blazor documentation or any tutorials that could solve the issue I'm having. This is in Blazor client-side and I am using Entity Framework Core
This is what my Course model looks like in the Shared project:
public class Course
{
public Guid CourseID { get; set; }
[Required]
public string CourseCode { get; set; }
[Required]
public string CourseName { get; set; }
public string CourseSubject { get; set; }
public string CourseCredits { get; set; }
}
This is what my Subject model looks like in the Shared project:
public class Subject
{
public Guid SubjectID { get; set; }
public string SubjectName { get; set; }
}
This is my CourseData Data Access Model in the Server project:
ApplicationDbContext db = new ApplicationDbContext ();
public IEnumerable<Course> GetAllCourses()
{
try
{
return db.Courses.ToList();
}
catch
{
throw;
}
}
public void AddCourse(Course course)
{
try
{
db.Courses.Add(course);
db.SaveChanges();
}
catch
{
throw;
}
}
This is my SubjectData Data Access Model in the Server project:
ApplicationDbContext db = new ApplicationDbContext ();
public IEnumerable<Subject> GetAllSubjects()
{
try
{
return db.Subjects.ToList();
}
catch
{
throw;
}
}
public void AddSubject(Subject subject)
{
try
{
db.Subjects.Add(subject);
db.SaveChanges();
}
catch
{
throw;
}
}
This is my Course Controller in the Server project:
CourseData objcourse = new CourseData();
[HttpGet]
[Route("api/Courses/Courses")]
public IEnumerable<Course> Index()
{
return objcourse.GetAllCourses();
}
[HttpPost]
[Route("api/Courses/Create")]
public void Create([FromBody] Course course)
{
if (ModelState.IsValid)
objcourse.AddCourse(course);
}
This is how I save the value in my Course creation page in my Client project:
#page "/Courses/Create"
#inject HttpClient Http
#inject Microsoft.AspNetCore.Components.NavigationManager NavigationManager
<h1>Add Course</h1>
<hr />
<div class="row">
<div class="col-md-4">
<div>
<div class="form-group">
<label for="CourseCode" class="control-label">Course Code</label>
<input for="CourseCode" class="form-control" #bind="#course.CourseCode" />
</div>
<div class="form-group">
<label for="CourseName" class="control-label">Course Name</label>
<input for="CourseName" class="form-control" #bind="#course.CourseName" />
</div>
<div class="form-group">
<label for="CourseSubject" class="control-label">Subject</label>
<select class="form-control" #bind="#course.CourseSubject">
<option></option>
#foreach (var subject in subjectList)
{
<option value="#subject.SubjectID">#subject.SubjectName</option>
}
</select>
</div>
<div class="form-group">
<label for="CourseCredits" class="control-label">Course Credits</label>
<input for="CourseCredits" class="form-control" #bind="#course.CourseCredits" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-default" #onclick="#CreateCourse">Save</button>
<button class="btn" #onclick="#cancel">Cancel</button>
</div>
</div>
</div>
</div>
#functions {
List<Subject> subjectList = new List<Subject>();
Course course = new Course();
protected override async Task OnInitializedAsync()
{
subjectList = await Http.GetJsonAsync<List<Subject>>("api/Subjects/Subjects");
}
protected async Task CreateCourse()
{
await Http.SendJsonAsync(HttpMethod.Post, "/api/Courses/Create", course);
NavigationManager.NavigateTo("/Courses/Courses");
}
void cancel()
{
NavigationManager.NavigateTo("/Courses/Courses");
}
}
And finally this is my Courses list page in my Client project where it returns the Guid for the subject name, for which I would like to show the subject name instead of it's Guid:
#page "/Courses/Courses"
#inject HttpClient Http
<h1>Courses</h1>
<p>
Create New
</p>
#if (courseList == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class='table'>
<thead>
<tr>
<th>Course Code</th>
<th>Course Name</th>
<th>Subject</th>
<th>Credits</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
#foreach (var course in courseList)
{
<tr>
<td>#course.CourseCode</td>
<td>#course.CourseName</td>
<td>#course.CourseSubject</td>
<td>#course.CourseCredits</td>
<td>
<a href='/Courses/Edit/#course.CourseID'>Edit</a> |
<a href='/Courses/Delete/#course.CourseID'>Delete</a>
</td>
</tr>
}
</tbody>
</table>
}
#functions {
Course[] courseList;
protected override async Task OnInitializedAsync()
{
courseList = await Http.GetJsonAsync<Course[]>
("/api/Courses/Courses");
}
}
Database Context as requested:
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Course> Courses { get; set; }
public virtual DbSet<Subject> Subjects { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(#"REMOVED-FOR-SECURITY");
}
}
}
Help would be much appreciated.
Other answers are good but let me give you something else to think about...
When writing Blazor apps try to think in terms of components more often than of code.
For example, let's say that the Subjects are static data that doesn't change a lot. So if every time you show a list, or want to build a dropdown for them, you're potentially going to have to make a database call or a SQL JOIN? So my first thought would be to create a lookup cache for GUID-to-Subject-name. And since this is Blazor if it's cached on the client you no longer need to make a database or API call - it's loaded once.
Then, since we're in Blazor, I'd create a <SubjectName> component, e.g.
Subject: <SubjectName Id="#Model.SubjectId" />
The component uses the cache to get the value, it can handle a null value, it can handle an invalid subject ID. You've encapsulated a lot of behaviour and error handling in a single place, and can re-use that every time you need to map an ID to a subject name.
Similarly I'd create a <SubjectDropdown> component to show a list of subjects, again using the cache.
You set CourseSubject with the SubjectID with is a Guid, so you get a Guid.
If you want to display the subject name either, request the subject by its Guid, or return the Subject in your course:
public class Course
{
public Guid CourseID { get; set; }
[Required]
public string CourseCode { get; set; }
[Required]
public string CourseName { get; set; }
public string CourseSubject { get; set; }
public string CourseCredits { get; set; }
public virtual Subject Subject { get; set; }
}
public IEnumerable<Course> GetAllCourses()
{
try
{
return db.Courses
.Include(c => c.Subject)
.ToList();
}
catch
{
throw;
}
...
}
#foreach (var course in courseList)
{
<tr>
<td>#course.CourseCode</td>
<td>#course.CourseName</td>
<td>#course.Subject.SubjectName</td>
<td>#course.CourseCredits</td>
<td>
<a href='/Courses/Edit/#course.CourseID'>Edit</a> |
<a href='/Courses/Delete/#course.CourseID'>Delete</a>
</td>
</tr>
}
I seem to be stuck at developing a custom Key/Value pair provider for Amazon's System Manager Parameter Store (SSM) using NETFramework 4.7.1 that utilizes Microsoft.Configuration.ConfigurationBuilders.
The implementation:
using System;
using System.Collections.Generic;
using Amazon.SimpleSystemsManagement;
using Amazon.SimpleSystemsManagement.Model;
using Microsoft.Configuration.ConfigurationBuilders;
using System.Linq;
using System.Diagnostics;
using System.Collections.Specialized;
using Amazon.Runtime;
using Amazon.Runtime.CredentialManagement;
using System.Configuration;
using System.Threading.Tasks;
namespace AXS.Configurations
{
public class ParameterStoreConfigBuilder : KeyValueConfigBuilder
{
public const string envTag = "Environment";
public const string appNameTag = "AppName";
private IAmazonSimpleSystemsManagement client;
/// <summary>
/// Gets or sets an environment (dev|qa|staging|production)
/// </summary>
public string Environment { get; set; }
/// <summary>
/// Gets or sets a AppName
/// </summary>
public string AppName { get; set; }
public ParameterStoreConfigBuilder(IAmazonSimpleSystemsManagement client,
string appName,
string environment)
{
this.client = client;
Environment = environment.ToLower();
AppName = appName;
}
public ParameterStoreConfigBuilder()
{
client = new AmazonSimpleSystemsManagementClient();
}
public override string Description => "Parameter Store";
public override string Name => "SSM";
protected override void LazyInitialize(string name, NameValueCollection config)
{
Optional = false;
base.LazyInitialize(name, config);
string env = UpdateConfigSettingWithAppSettings(envTag);
if (string.IsNullOrWhiteSpace(env))
throw new ArgumentException($"environment must be specified with the '{envTag}' attribute.");
Environment = env;
string appName = UpdateConfigSettingWithAppSettings(appNameTag);
if (string.IsNullOrWhiteSpace(appName))
throw new ArgumentException($"appName must be specified with the '{appNameTag}' attribute.");
AppName = appName;
client = new AmazonSimpleSystemsManagementClient("","", Amazon.RegionEndpoint.USWest2);
}
public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
{
Trace.TraceInformation($"return values prefix {prefix}");
if (client == null)
return null;
var parameters = new List<Parameter>();
string nextToken = null;
do
{
var response = client.GetParametersByPath(new GetParametersByPathRequest { Path = prefix, Recursive = true, WithDecryption = true, NextToken = nextToken });
nextToken = response.NextToken;
parameters.AddRange(response.Parameters);
} while (!string.IsNullOrEmpty(nextToken));
return parameters.Select(p => new
{
Key = p.Name,
p.Value
}).ToDictionary(parameter => parameter.Key, parameter => parameter.Value, StringComparer.OrdinalIgnoreCase);
}
public override string GetValue(string key)
{
return Task.Run(async () => { return await GetValueAsync(key); }).Result;
}
private async Task<string> GetValueAsync(string key)
{
var name = $"/{Environment}/{AppName}/{key.Replace(':', '/')}";
Trace.WriteLine($"get value async:{name}");
if (client == null)
return null;
try
{
Trace.TraceInformation($"fetch key {name}");
var request = new GetParameterRequest
{
Name = name,
WithDecryption = true
};
var response = await client.GetParameterAsync(request);
var parameter = response.Parameter;
var value = parameter.Type == ParameterType.SecureString ? "*****" : parameter.Value;
Trace.TraceInformation($"fetched name={name} value={value}");
return value;
}
catch (Exception e) when (Optional && ((e.InnerException is System.Net.Http.HttpRequestException) || (e.InnerException is UnauthorizedAccessException))) { }
return null;
}
}
}
The problem seems to be that AWS SSM client never gets created.
If I change the code and try to instantiate in the constructor I get a stack overflow exception due to recursion.
Any ideas on how to force to get AmazonSimpleSystemsManagementClient created?
The code uses guidance from https://github.com/aspnet/MicrosoftConfigurationBuilders
The App.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection,
System.Configuration, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
restartOnExternalChanges="false"
requirePermission="true" />
</configSections>
<configBuilders>
<builders>
<add name="ParameterStore" Environment="development" AppName="myAppNameforParmStore" type="AXS.Configurations.ParameterStoreConfigBuilder, AXS.Configurations" />
<add name="Env" prefix="appsettings_" stripPrefix="true" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment, Version=2.0.0.0, Culture=neutral" />
</builders>
</configBuilders>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
</startup>
<appSettings configBuilders="Env,ParameterStore">
<add key="Url" value="URL Value for from paramter Store" />
<add key="Secret" value="Some Secret value decrypted" />
</appSettings>
</configuration>
Thanks
UPDATE
I posted an updated version of the AwsSsmConfigurationBuilder, and a sample ASP.NET Web Forms project that uses it, on my GitHub:
https://github.com/Kirkaiya/AwsSsmConfigBuilderPoC/
Disclaimer: This is a proof-of-concept (POC) for a custom ConfigurationBuilder for ASP.NET 4.7.1 or higher (running on .NET Framework obviously). It's a POC, so it doesn't do anything besides allow you store Configuration AppSettings in AWS Parameter Store (a feature of Simple Systems Manager). So, clearly, don't use this in production without productizing and testing it!
Prerequisites:
Your project must target .NET Framework 4.7.1 or higher
Include NuGet package Microsoft.Configuration.ConfigurationBuilders.Base
Have parameters in AWS SSM Parameter Store that have the same name (not counting the prefix) as parameters in your web.config file, and vice-versa.
Notes
In order to avoid recursively calling a concrete constructor or Initialize, I used a static constructor to instantiate the AmazonSimpleSystemsManagementClient, which is held in a static member.
Web.Config additions
Note: change the assembly/class-name of your builder to match yours, etc.
<configSections>
<section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false" />
</configSections>
<configBuilders>
<builders>
<add name="ParameterStore" ssmPrefix="/padnugapp/ApiKeys" type="Microsoft.Configuration.ConfigurationBuilders.AwsSsmConfigBuilder, AspNetWebFormsSample" />
</builders>
</configBuilders>
<appSettings configBuilders="ParameterStore">
<add key="TestKey" value="TestKey Value from web.config" />
<add key="TwitterKey" value="TwitterKey value from web.config" />
</appSettings>
And the AwsSsmConfigBuilder.cs file:
namespace Microsoft.Configuration.ConfigurationBuilders
{
public class AwsSsmConfigBuilder : KeyValueConfigBuilder
{
private string BaseParameterPath = "/padnugapp/ApiKeys";
private static IAmazonSimpleSystemsManagement _client;
static AwsSsmConfigBuilder()
{
_client = new AmazonSimpleSystemsManagementClient();
}
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
if (config["ssmPrefix"] == null)
return;
BaseParameterPath = config["ssmPrefix"];
}
public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
{
if (_client == null)
return null;
var request = new GetParametersByPathRequest
{
Path = $"{BaseParameterPath}/{prefix}",
WithDecryption = true,
};
var response = _client.GetParametersByPathAsync(request).Result;
var result = response.Parameters.ToDictionary(param => param.Name, param => param.Value, StringComparer.OrdinalIgnoreCase);
return result;
}
public override string GetValue(string key)
{
if (_client == null)
return null;
var request = new GetParameterRequest
{
Name = $"{BaseParameterPath}/{key}",
WithDecryption = true,
};
var response = _client.GetParameterAsync(request).Result;
return response.Parameter.Value;
}
}
}
The code I put into a web-forms (.aspx) page that renders the two appSettings items in HTML:
TestKey =
<%=(System.Configuration.ConfigurationManager.AppSettings["TestKey"]) %>
<br />
TwitterKey =
<%=(System.Configuration.ConfigurationManager.AppSettings["TwitterKey"]) %>
I can't stress enough that this is just for a demo I'm doing, and not tested in any way, shape or form except on my laptop ;-)
I am not able to store a cookie value in a cookie. Here is my code:
namespace CookieCreation.Controllers
{
public class CookieController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult WriteCookie(String CookieName, String CookieValue, bool IsPersistent)
{
if (IsPersistent)
{
CookieOptions cookies = new CookieOptions();
cookies.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Append(CookieName, CookieValue, cookies);
}
else
{
Response.Cookies.Append(CookieName, CookieValue);
}
ViewBag.Message = ("Sucessful");
return View("Index");
}
public IActionResult ReadCookie()
{
ViewBag.CookieValue = Request.Cookies["CookieName"];
return View();
}
}
}
How do you determine that the cookie is not stored? Use Chrome developer tools, Application/Cookies to see if the cookie is created.
I used your code with the Index.cshtml below and everything worked, cookie is stored and read using your code:
<h1>Index</h1>
<p>Last operation: #ViewBag.Message</p>
#{
using(Html.BeginForm("WriteCookie", "Cookie", new { CookieName = "CookieName", IsPersistent = false}, FormMethod.Post )) {
<input type="text" value="42" name="CookieValue" />
<input type="submit" value="post"/>
}
}
I have a simple NancyModule that has a Post declared:
Post["/Car/New"] = args =>
{
Car newCar = this.Bind<Car>();
newCar = _carRepos.CreateNewCar(newCar);
return Response.AsJson<Car>(newCar);
};
Posting to this from a view works fine:
<form action="/Car/New" method="post">
<input type="text" name="colour" />
<input type="submit" value="Submit" />
</form>
When I try and run a test for this route, I get the following error:
System.Exception : ConfigurableBootstrapper Exception
----> Nancy.RequestExecutionException : Oh noes!
----> System.MissingMethodException : Method not found: '!!0 Nancy.ModelBinding.ModuleExtensions.Bind(Nancy.INancyModule, System.String[])'.
Result StackTrace:
at Nancy.Testing.PassThroughStatusCodeHandler.Handle(HttpStatusCode statusCode, NancyContext context)
at Nancy.NancyEngine.CheckStatusCodeHandler(NancyContext context)
at Nancy.NancyEngine.HandleRequest(Request request, Func`2 preRequest)
at Nancy.NancyEngine.HandleRequest(Request request)
at Nancy.Testing.Browser.HandleRequest(String method, String path, Action`1 browserContext)
at Nancy.Testing.Browser.Post(String path, Action`1 browserContext)
at Shopr.Tests.Cars.CarTests.PostNewCarReturnsCar() in c:\Users\*******\Documents\Visual Studio 2012\Projects\Shopr\Shopr.Tests\Cars\CarTests.cs:line 35
--RequestExecutionException
at Nancy.NancyEngine.InvokeOnErrorHook(NancyContext context, ErrorPipeline pipeline, Exception ex)
--MissingMethodException
at Shopr.Api.Modules.CarsModule.<.ctor>b__3(Object args)
at Nancy.Routing.Route.Invoke(DynamicDictionary parameters)
at Nancy.Routing.DefaultRouteInvoker.Invoke(Route route, DynamicDictionary parameters, NancyContext context)
at Nancy.Routing.DefaultRequestDispatcher.Dispatch(NancyContext context)
at Nancy.NancyEngine.InvokeRequestLifeCycle(NancyContext context, IPipelines pipelines)
And this is my test:
[Test]
public void PostNewCarReturnsCar()
{
var browser = BrowserFactory.Create();
var response = browser.Post("/Car/New", with =>
{
with.FormValue("Colour", "Red");
});
var car = GetObjectFromJsonBody(response.Body.AsString());
Assert.IsNotNull(car);
Assert.AreEqual(2, car.Id);
}
This is my testing Bootstrapper:
public class NancyBootstrapper : ConfigurableBootstrapper
{
public NancyBootstrapper()
: base(with => { with.Module<CarsModule>(); })
{ }
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
container.Register<ICarRepository>(new FakeData.CarRepository());
}
}
Do I have to do anything special in my ConfigurableBootstrapper to get the binding to work?
Double check that your packages.config are pulling the right versions.
UPDATE: As this mostly came from #StevenRobbins I award him a pat on the back:
The suggestion from #Jon to check packages.config was correct. Even though the packages were added within minutes of each other using the same method, the test project had an older version of Nancy than the web project. Updated to the proper version and it works fine now.