MVC 3 getting values from AppSettings in web.config - asp.net-mvc

In normal ASP.NET Web forms sites I would use web.configs "appsettings" to add application setting data to a site. However, I am not able to retrieve setting values this way when using MVC 3.
First off, there are 2 web.config files. One in the root of the site, the second is listed in the Views area. I assume I want to put my appsettings information in the root web.config file, correct? (putting it in the other under views seems to produce an error stating "AppSettings" can only appear once per web application.)
When I try to retrieve it (C#: System.Configuration.ConfigurationManager.AppSettings["SettingName"]) I get a blank or empty/null return value. What am I doing wrong?
I should mention that I am actually retrieving this information in a Class file under the Models area for set specific values for a model using get; set;. Is it possible that I'm not allowed to do this in Models?
In a Controller.cs:
WindowsLiveConnect.ServiceConfiguration WLSC = new WindowsLiveConnect.ServiceConfiguration();
ViewBag.ClientID = SC.ClientID; // This returns empty
In web.config
...
<appSettings>
<add key="webpages:Version" value="1.0.0.0"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
<add key="ClientID" value="0000000040062A3F" />
<add key="ClientSecret" value="SUPERSECRETPASSWORD" />
<add key="RedirectURL" value="http%3A%2F%2Fwww.quilnet.com" />
</appSettings>
...
In the Model.cs file:
public class ServiceConfiguration
{
private string clientid;
private string clientsecret;
private string redirecturl;
public string ClientID
{
get { return clientid; }
set
{
clientid = System.Configuration.ConfigurationManager.AppSettings["ClientID"];
}
}
public string ClientSecret
{
get { return clientsecret; }
set
{
clientsecret = System.Configuration.ConfigurationManager.AppSettings["ClientSecret"];
}
}
public string RedirectURL
{
get { return redirecturl; }
set
{
redirecturl = System.Configuration.ConfigurationManager.AppSettings["RedirectURL"];
}
}
}

Usually I'm using AppSettings static class to access those parameters. Something like this:
public static class AppSettings
{
public static string ClientSecret
{
get
{
return Setting<string>("ClientSecret");
}
}
private static T Setting<T>(string name)
{
string value = ConfigurationManager.AppSettings[name];
if (value == null)
{
throw new Exception(String.Format("Could not find setting '{0}',", name));
}
return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
}
}

Are you ever calling set? I'm guessing it never gets called, so the private variable never gets the value from the config.
Try it this way (just retrieve the value in the get, no set needed):
public string ClientSecret
{
get { return System.Configuration.ConfigurationManager.AppSettings["ClientSecret"]; }
}

I did it this way:
myVar = System.Configuration.ConfigurationManager.AppSettings["ClientID"].ToString();

Looking at the code I assume you are using sharepoint provider hosted apps?
Best thing to do here in mvc is to ignore the web.config which is on the view level and only use the one in the root of the webapplication.
The other thing I want to mention is that its probably not a good idea to fetch configuration information from the web.config in the actual model.
Its better to move it either to :
- the constructor of the controller
- the factory/repository which returns this model

Related

Serving static files in ASP.NET to authorized users only

I am trying to secure a folder under my project that just has some static files, a combination of .htm and .js files. I have tried creating a custom HttpHandler like:
public class StaticFilesHttpHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.IsAuthenticated)
{
// continue with the request
}
else
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
public bool IsReusable => false;
}
Then register it to be used with a route via Route.Config
routes.RouteExistingFiles = true;
routes.Add("helpRoute", new Route("folder/*.htm", new StaticFilesRouteHandler ()));
and a route handler to provide the
public class StaticFilesRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext context)
{
return new StaticFilesHttpHandler ();
}
}
and also via web.config under system.webServer
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="StaticFilesHttpHandler "/>
</handlers>
Files in the folder are provided by a 3rd party. I am to call a function inside a js file in the folder which then redirects the user to a proper .htm file inside it's sub structure. I do not want users to be able to type the url and access any of the files. What am I doing wrong?
can you change the type to TransferRequestHandler and make sure your path is correct.
<handlers>
<add name="StaticFileHandler" verb="GET" path="~/help/default.htm" type="TransferRequestHandler" />
</handlers>
in your global.asax file you can access the request in Application_BeginRequest to verify if the request is authenticated or not.

Strongly typed AppSettings Configuration in ASP.NET 5

When using WebApi 2 my web.config was
<connectionStrings>
<add name="RavenHQ" connectionString="Url=http://localhost:8080;Database=ModelFarmDb" />
</connectionStrings>
For ASP.NET 5.0, I can't work out how to write the config.json file to do the same thing.
I've tried
{
"Data": {
"RavenHQ": {
ConnectionString: "Url=http://localhost:8080;Database=ModelFarmDb"
}
}
}
but it doesn't work. Any suggestions on how to directly map the web.config sections to config.json so as not to break other libraries that assume a web.config?
The app is running under IIS Express locally and is a web app on Azure.
Many thanks!
You accomplish this in asp.net 5.0 in a different way. I used json file for this example. If you need add xml file just use these package Microsoft.Framework.Configuration.Xml and use .AddXmlFile() method
This example uses beta 7
Create an AppSetting class
public class AppSetting
{
public string Setting1 { get; set; }
public string Setting2 { get; set; }
}
In your startup file add the json file with the configuration on this example is call config.json
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("config.json")
.AddXmlFile("thefilename")
.AddJsonFile($"config.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
Then you need to add the app service your AppSetting class so it can be injected
for later use
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
Then in your controller or where ever you need the configuraton inject the IOptions<AppSettings>
public class SampleController : Controller
{
private readonly AppSettings _appSettings;
public SampleController(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings.Value;
}
}
and this is how the json looks like
{
"AppSetting": {
"Setting1": "Foo1",
"Setting1": "Foo1"
}
}
I took these peaces of code from live.asp.net in github

Creating EF controls in MVC with enums as keys

Ok so here's some context: I'm using EF5, MVC4 and SQL CE4 to build up a web application. I've been loosely following this tutorial with a few differences.
My context class and POCO objects are in their own assembly.
I'm using SQL CE4 instead of SQL Express Local DB
My classes aren't as simple as the tutorial
I've already used a workaround to get simple classes to work register.
I had thought using enums in EF5 was supported in EF5, but can they be used in Keys?
When I try to add a control (Add Controller, MVC controller with read/write actions and views, using Entity Framework) for a simple class (1 int key property, 1 string property), it works.
I get varied errors when trying to add a class that has a property which is part of a key (primary or foreign)
Unable to retrieve metadata for 'blah'. Using the
same DbCompiledModel to create contexts against different types of
database servers is not supported. Instead, create a separate
DbCompiledModel for each type of server being used.
Index was out of range. Must be non-negative and less than the size of
the collection.
Parameter name: index
C:\Program Files (x86)\Microsoft Visual Studio\11.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 4\
CodeTemplates\AddController\ControllerWithContext.tt(0,0) : error :
Running transformation: System.IndexOutOfRangeException: Index was outside the bounds of the
array.
---StackTrace---
The only similarities I've found between these classes is that they have an emun that's tied to a key. Other classes with non-key enums generate correctly. Is this the issue or have I completely missed the mark?
Edit: Example of a class which fails
public class A
{
public virtual AIdEnum Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
public virtual ICollection<C> Cs { get; set; }
}
Ok, so I've just ran this up quickly with SQL CE 4 and it appears to work great:
public class EnumTestContext : DbContext
{
public EnumTestContext() : base("CEDB")
{
}
public DbSet<MyClass> MyClasses { get; set; }
}
public enum MyEnum
{
EnumValue1,
EnumValue2
}
public class MyClass
{
[Key, Column(Order = 0)]
public MyEnum MyEnumKey { get; set; }
[Key, Column(Order = 1)]
public int MyIntKey { get; set; }
[Column(Order = 2)]
public string Name { get; set; }
}
I then add an entity like this:
using (var context = new EnumTestContext())
{
context.MyClasses.Add(new MyClass()
{
MyEnumKey = MyEnum.EnumValue1,
MyIntKey = 22,
Name = "Hello World"
});
context.SaveChanges();
}
This is all working fine for me - does this help?
You need put this line:
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
before the DbContext lifecicle beggin.
A exemple that you can to download in the MSDN Gallery
It seems my issue is that Controller creation doesn't work with SQLCE 4.0 connectionStrings so using a conectionString of provider of System.Data.SqlClient handled that issue.
The next problem I had was that connectionString values (such as encryption) were not respected through this means so I now have 2 different constructors to get around this bug.
#if DEBUG
public Context()
: base("name=DefaultConnection")
{ ; }
#else
/// <summary>
/// Default Constructor
/// </summary>
public Context()
: base("name=CompactConnection")
{ ; }
#endif
Here's my configuration:
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
<parameters>
<parameter value="System.Data.SqlServerCe.4.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
<connectionStrings>
<add name="CompactConnection" providerName="System.Data.SqlServerCe.4.0"
connectionString="Data Source="|DataDirectory|\DB.sdf";encryption mode=platform default;password="P4$$w0rd!""/>
<add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MsSqlCe-20121028004432;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MsSqlCe-20121028004432.mdf" />
</connectionStrings>
Of course, this is just a workaround for a deeper issue. If anyone else knows of the root cause of this issue, I'd love to know.

accessing profile.newproperty in MVC web applications

I recently asked this question How to persist anon user selection (ex: theme selection). and started to learn about ASP.NET profiles and their properties in the web config. I tried the answer from the link but i was unable to access profile.newproperty
How to assign Profile values?
This question specifies that web-applications don't support profile out of the box and a custom model based on ProfileBase must be created. The question was answered in 2009 and I wanted to know if this is still the same case.
In a ASP.NET 4.0 web application can I access profile.newproperty with a property i defined in the section in the web.config without needing to code C# except when accessing it.
I just saw your question, yes you are right, the answer I posted was related to web sites and therefore, it doesn't work with Web Applications or MVC
Here I will show you code to work with profiles in MVC using anonymous and authenticated user profiles
Output
Anonymous user - No profile set yet
Anonymous user - profile set
Authenticated user - profile migrated
Web.config
<anonymousIdentification enabled="true"/>
<profile inherits="ProfileInWebApplicationMVC1.UserProfile">
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
</profile>
UserProfile class
public class UserProfile : ProfileBase
{
public static UserProfile GetProfile()
{
return HttpContext.Current.Profile as UserProfile;
}
[SettingsAllowAnonymous(true)]
public DateTime? LastVisit
{
get { return base["LastVisit"] as DateTime?; }
set { base["LastVisit"] = value; }
}
public static UserProfile GetProfile(string userID)
{
return ProfileBase.Create(userID) as UserProfile;
}
}
Home controller
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
var p = UserProfile.GetProfile();
return View(p.LastVisit);
}
[HttpPost]
public ActionResult SaveProfile()
{
var p = UserProfile.GetProfile();
p.LastVisit = DateTime.Now;
p.Save();
return RedirectToAction("Index");
}
Index view
#if (!this.Model.HasValue)
{
#: No profile detected
}
else
{
#this.Model.Value.ToString()
}
#using (Html.BeginForm("SaveProfile", "Home"))
{
<input type="submit" name="name" value="Save profile" />
}
And finally, when you are an anonymous user you can have your own profile however, once you register to the site, you need to migrate your current profile to be used with your new account. This is because ASP.Net membership, creates a new profile when a user logs-in
Global.asax, code to migrate profiles
public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs args)
{
var anonymousProfile = UserProfile.GetProfile(args.AnonymousID);
var f = UserProfile.GetProfile(); // current logged in user profile
if (anonymousProfile.LastVisit.HasValue)
{
f.LastVisit = anonymousProfile.LastVisit;
f.Save();
}
ProfileManager.DeleteProfile(args.AnonymousID);
AnonymousIdentificationModule.ClearAnonymousIdentifier();
Membership.DeleteUser(args.AnonymousID, true);
}

MVC3 custom outputcache

I'd like to use caching in my application but the data I'm returning is specific to the logged in user. I can't use any of the out of the box caching rules when I need to vary by user.
Can someone point me in the right direction on creating a custom caching attribute. From the controller I can access the user from Thread.CurrentPrincipal.Identity; or a private controller member that I initialize in the controller constructor _user
Thank you.
You could use the VaryByCustom. In Global.asax override the GetVaryByCustomString method:
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if (arg == "IsLoggedIn")
{
if (context.Request.Cookies["anon"] != null)
{
if (context.Request.Cookies["anon"].Value == "false")
{
return "auth";
}
else
{
return "anon";
}
}
else
{
return "anon";
}
}
else
{
return base.GetVaryByCustomString(context, arg);
}
}
and then use the OutputCache attribute:
[OutputCache(CacheProfile = "MyProfile")]
public ActionResult Index()
{
return View();
}
and in web.config:
<caching>
<outputcachesettings>
<outputcacheprofiles>
<clear />
<add varybycustom="IsLoggedIn" varybyparam="*" duration="86400" name="MyProfile" />
</outputcacheprofiles>
</outputcachesettings>
</caching>
The Authorize Attribute has some interesting things going on regarding caching for authorized vs unauthorized users. You may be able to extract it's logic and modify it to cache per authorized user, instead of just per-"the user is authorized".
Check out this post:
Can someone explain this block of ASP.NET MVC code to me, please?
You should use OutputCache.VaryByCustom Property to specify custom vary string. And to use it, you should override method in your Global.asax
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if(arg.ToLower() == "currentuser")
{
//return UserName;
}
return base.GetVaryByCustomString(context, arg);
}

Resources