ASP.NET Output Cache Provider for Redis doesn't store - asp.net-mvc

I am not sure how to troubleshoot it but I am trying to implement ASP.NET Output Cache via Redis Output cache provider.
We have Redis server (non-azure) set up and I can store cache for general usage.
However, when I try to setup ASP.NET output cache, it doesn't seem to save anything to cache!
I have installed Microsoft.Web.RedisOutputCacheProvider via Nuget.
Web.Config is set up with following:
<caching>
<outputCache defaultProvider="MyRedisOutputCache">
<providers>
<add name="MyRedisOutputCache" type="Microsoft.Web.Redis.RedisOutputCacheProvider" host="ServerName" port="6464" accessKey="PasswordToRedis" />
</providers>
</outputCache>
</caching>
The MVC controller is setup with OutputCache attribute:
[OutputCache(Duration = 3600, VaryByParam = "*", Location = OutputCacheLocation.ServerAndClient)]
public JsonResult GetLookupData()
When I check the Redis, I don't see any OutputCache being stored.
Am I missing something? Is there a way to debug why it is not storing anything in cache?

Ok this was really silly.
When you install RedisOutputCacheProvider via Nuget, you will get this small doco in your app/web.config:
<!-- For more details check https://github.com/Azure/aspnet-redis-providers/wiki --><!-- Either use 'connectionString' OR 'settingsClassName' and 'settingsMethodName' OR use 'host','port','accessKey','ssl','connectionTimeoutInMilliseconds' and 'operationTimeoutInMilliseconds'. --><!-- 'databaseId' and 'applicationName' can be used with both options. --><!--
<add name="MyRedisOutputCache"
host = "127.0.0.1" [String]
port = "" [number]
accessKey = "" [String]
ssl = "false" [true|false]
databaseId = "0" [number]
applicationName = "" [String]
connectionTimeoutInMilliseconds = "5000" [number]
operationTimeoutInMilliseconds = "1000" [number]
connectionString = "<Valid StackExchange.Redis connection string>" [String]
settingsClassName = "<Assembly qualified class name that contains settings method specified below. Which basically return 'connectionString' value>" [String]
settingsMethodName = "<Settings method should be defined in settingsClass. It should be public, static, does not take any parameters and should have a return type of 'String', which is basically 'connectionString' value.>" [String]
loggingClassName = "<Assembly qualified class name that contains logging method specified below>" [String]
loggingMethodName = "<Logging method should be defined in loggingClass. It should be public, static, does not take any parameters and should have a return type of System.IO.TextWriter.>" [String]
redisSerializerType = "<Assembly qualified class name that implements Microsoft.Web.Redis.ISerializer>" [String]
/>
It indicates the default value for "ssl" would be false.
However, reading through the code itself, it is actually defaulted to true.
So explicitly setting ssl to false have fixed it.
EDIT
Oh and I had to downgrade RedisOutputCacheProvider to 1.7.5.
3.0.1 didn't work at all for me.

I have mine working on local host without the SSL setting. In production I require it, but on mine it is part of the connectionstring (that I was given by my Redis hosting service).
The reason it was not working and you had to downgrade RedisOutputCacheProvider to 1.7.5 is because you are using Exchange.Redis.Strongname.dll (version 1.2.6)
As per this issue, Redis no longer requires a strongname because the base version is now strong named.
https://github.com/Azure/aspnet-redis-providers/issues/107
So to use RedisOutputCacheProvider v3.0.144 you need to uninstall Exchange.Redis.Strongname.dll (version 1.2.6) and install Exchange.Redis.dll (version 2.0.601) via Nuget

Related

How can I know if my app is running under Kestrel or HTTP.sys?

What's the best way to know if my app is running under Kestrel or HTTP.sys. All that I have found so far is to check for "Kestrel" in one of the HttpConext property class names.
Within an MVC controller I can do something like this:
Boolean IsKestrel = HttpContext.Features.GetType().ToString().Contains("Kestrel");
i.e. check this:
Features = {Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection<Microsoft.AspNetCore.Hosting.HostingApplication.Context>}
When using HTTP.sys I only get:
Features = {Microsoft.AspNetCore.Http.Features.FeatureCollection}
(Is "Http" here enough to know that this is HTTP.sys?)
There must be a better way. Is there an obvious property somewhere that contains the name of the host being used?
A broader question might be, how do I know what the builder pattern built?
Update
Found something better, but still looking for a Property that has the server name or type.
In an MVC controller:
var isKestrel = HttpContext.Request.Headers.GetType().ToString().Contains(".Kestrel.");
var isHTTPsys = HttpContext.Request.Headers.GetType().ToString().Contains(".HttpSys.");
At the operating system level, netsh http show servicestate will list all active URLs listening via HTTP.SYS.
From code you can locate an instance of Microsoft.AspNetCore.Hosting.Server.IServer and check what its implementation is, in netcore 6:
Kestrel => Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl
IIS ==> Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer
HTTP.SYS => Microsoft.AspNetCore.Server.HttpSys.MessagePump
This relies on implementation details (so can break), also other extensions can change these e.g. CoreWcf creates CoreWCF.Configuration.WrappingIServer that wraps one of the above implementations.
you can use System.Diagnostics.Process.GetCurrentProcess().ProcessName
I am not sure whether you want to check this information using the code only or you are just looking for a way to know on which web server your app is running.
In my search result, I found that we could set the ports for a specific web server. When the application will run on that specific web server then it will use that pre-configured port. I am assuming your app also has a similar configuration. You could set the different ports for Kestrel, Http.sys, or IIS. By checking the port number you could say that on which web server your site is running.
You could try to go to the launchSettings.json file in your project where you could configure ports for IIS and Kestral.
Helpful References:
Kestrel Web Server in ASP.NET Core
Understand HTTP.sys Web Server In ASP.NET Core
Hello this is a good question, you question is asking how to find out from inside the code and not from a console.
OOB I did not find anything. So, I had to get very creative to figure this out, sorry for the typo's its brand new stuff...
Option 1:
Since the Kestrel section & endpoints are inside the appsettings.json I used that to find out if its hosted by Kestrel!
//Please create a static class to hold the config.
public static class MyStartupIsItKestrelConfiguration
{
public static IConfiguration Configuration;
public bool static IsKestrel()
{
//check your section kestrel??
var kestrel = configuration.GetSection("Kestrel");
// now check kestrel section or any other section
// see picture for kestrel endpoint in app setting sbelow
return true;
}
}
Now you can access it anywhere and see if you used Kestrel
//Now add it/save it in your startup and access later
public Startup(IConfiguration configuration)
{
Configuration = configuration;
MyStartupIsItKestrelConfiguration.Configuration = configuration;
}
Once you have this
//you can use it in ** YOUR CONTROLLER
MyStartupIsItKestrelConfiguration.IsKestrel();
Option 2:
Please check this public Microsoft.AspNetCore.Http.Features.IFeatureCollection Features { get; }
You can get the features public TFeature? Get<TFeature> (); as a Key Value Pair - and then check the feature for e.g. KESTREL DOES NOT ALLOW PORT SHARING
they split the features namespace in .net core 6 there are breaking changes
You should use the features collection

How to solve MVC 5 Update and Delete Error

I have built MVC 5 Web application in .net 4.7.2. It runs fine in my local machine. Update and Delete commands were failing, which I have now sorted out thanks to this post: DELETE/PUT verbs result in 404 Not Found in WebAPI, only when running locally, I added this line to the web.config and all the CRUD now works:
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0
However, when I publish the application to my hosting server it breaks at this line in the web.config.
When I remove the line only Create and Retrieve data works, but Update and Delete fails with the error:
Object reference not set to an instance of an object.
at this line in the Index view: Line 40: #For Each item In Model
I understand this is because the model is null/nothing at this point.
This is my delete function showing where the error starts...
Public Function Delete(ByVal id As Integer) As ActionResult
Dim Client As HttpClient = New HttpClient()
Client.BaseAddress = New Uri(myWeb & "AppMenuCRUD")
Dim APIConsumer = Client.DeleteAsync("AppMenuCRUD/" & id.ToString())
APIConsumer.Wait()
Dim Result = APIConsumer.Result << failure here: Error 404
If Result.IsSuccessStatusCode Then
Return RedirectToAction("Index")
End If
Return View("Index")
End Function
The hosted server is running Windows Server 2016, .Net 4.7.2. I have enabled read/write to the website folder.
This is my IIS Settings in the hosting server:
UPDATES:
Having looked further into this, my hosting server is now updated to .net 4.8 and everything now just fails. I am now not able to even load the index view page. It breaks at the same line 40 as above.
In IIS, my application pool is set to integrated, .net 4.0. I cannot see anything higher from the dropdown list.
In IIS, I have nothing filtered in the list of HTTP Verbs, which I believe means it should accept anything.
UPDATE 2
After the updates to .net 4.8, I am now getting a new error
Lock Violation
I have enabled everything in the IIS, including changing all the ASP Configurations from Read Only to Read/Write.
I'm fearing I may introduce vulnerabilities to the VPS...
Is there anything else I need to do to get this to work?
Sometimes you need to update manually the Web.config when you change the target framework, please double check those lines:
<system.web>
<compilation debug="true" targetFramework="4.8">
<httpRuntime targetFramework="4.8" />
...
So, this is how I finally got it to work:
I am hosted with Plesk as 'control cpanel'. Apparently, Plesk has some lock which I had to unlock here:
Plesk > Tools & Settings > Security > Prohibit the ability to override handlers
Thanks to https://support.plesk.com/hc/en-us/articles/115000287305-Site-shows-500-19-Internal-Server-Error-Config-Error-Lock-violation. I was quite surprised my host could not help with this!
In the gymnastics of enabling everything else in my VPS, I ended up enabling/installing WebDAV. This also apparently does not 'allow' Edit/Delete actions. Instead of uninstalling it, I sorted it out from the web.config like this:
<system.webserver>
...
Thanks to Web API Put Request generates an Http 405 Method Not Allowed error

Runtime Error using Redis Cache Session State Provider

So I have an MVC application in which I am trying to use Azure's Redis Cache for my Session State Provider. With everything coded and configured and all, when I publish it, the index page loads fine. The only button to hit is 'Next', which is supposed to add a session state variable with a value, and then move on to the appropriate page. But when I click 'Next' I get a runtime error. If I simply comment out the sessionState block in Web.config and publish it like that, I can move on to the 'next' page just fine. So I'm wondering what is wrong with my use of the provider and why it's not working?
Web.Config block:
<system.web>
<compilation debug="false" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
<sessionState mode="Custom" customProvider="MySessionStateStore">
<providers>
<add
type="Microsoft.Web.Redis.RedisSessionStateProvider"
name="MySessionStateStore"
host = "[Host name from Azure]"
port = "[Port # from Azure]"
accessKey = "[Key from Azure]"
ssl = "false"
throwOnError = "true"
retryTimeoutInMilliseconds = "5000"
databaseId = "0"
applicationName = ""
connectionTimeoutInMilliseconds = "5000"
operationTimeoutInMilliseconds = "1000"
/>
</providers>
</sessionState>
</system.web>
POST function when I hit the 'Next' button:
<HttpPost()>
<ValidateAntiForgeryToken()>
Async Function Index(ByVal obj As Type) As Task(Of ActionResult)
If ModelState.IsValid Then
Session("VarName") = obj
Return RedirectToAction("nextPage", "[controller]")
End If
Return View()
End Function
Note I am not using any cookies, nor am I trying to use the Redis Cache for anything else. The non-SSL port IS Enabled in Azure (yes, bad, I know - that will change).
I hope that's enough to go on to be able to help - if not, let me know. Thank you!
OK, well, figures that after posting this I'd find the answer!
So there is this very small note at the bottom of one of the articles I could find on Redis Session State Provider: https://msdn.microsoft.com/en-us/library/azure/dn690522.aspx
"Note that data stored in the cache must be serializable, unlike the data that can be stored in the default in-memory ASP.NET Session State Provider. When the Session State Provider for Redis is used, be sure that the data types that are being stored in session state are serializable."
The Type that I was trying to put into the session variable was a custom type. I had to add the "Serializable" attribute to my class!
Once I published it as serializable, then voila!

What is the correct way to put parameters in web.config in MVC - ASP.NET

I have create this mail send form with MVC 4 in ASP.NET. Now I whant to put (use) parameters for smpt, username, password and port number in my Web.config. I have try this solution:
In Web.config:
<add key="smptserver" value="smptABC" />
<add key="username" value="usernameABC" />
<add key="password" value="passwordABC"/>
<add key="portNum" value="123"/>
In my Controller.cs I have this:
using System.Configuration;
...
string host = ConfigurationManager.AppSettings["smptserver"];
string user = ConfigurationManager.AppSettings["username"];
string pass = ConfigurationManager.AppSettings["password"];
string port = ConfigurationManager.AppSettings["portNum"];
using (var client = new SmtpClient
{
Host = host,
Port = port,
EnableSsl = true,
Credentials = new NetworkCredential(user, pass),
DeliveryMethod = SmtpDeliveryMethod.Network
})
{ ...
I have also found this solution (all same, but only this is changed):
string host = ConfigurationSettings.AppSettings["smptserver"];
string user = ConfigurationSettings.AppSettings["username"];
string pass = ConfigurationSettings.AppSettings["password"];
string port = ConfigurationSettings.AppSettings["portNum"];
[other is same]
What are the diffrences between "ConfigurationManager" and "ConfigurationSettings"?
Is this a proper way to set parameters with Web.config?
Thanks for explanation.
According to the documentation, ConfigurationSettings exposes deprecated (marked as "obsolete") functionality. (Your code should be generating warning messages at compile time to indicate this. If it's not, turn warnings back on.)
ConfigurationManager is preferred.

IIS Express defaulting to port 44300 for HTTPS when enabling SSL

When you initially set up IIS Express to enable SSL, it defaults the port to 44300. Unfortunately, when I try to access my site in on https://localhost/ it doesn't work unless I use the port number 44300 - https://localhost:44300/.
The links are generated using the following:
<%= Html.ActionLink("Index", "Index", "Home", new { #action = "https://" + Request.Hostname + Url.Action("Index", "Home") }) %>
Although an ugly solution, the #action keyword can override the generated route, but it means that the application would seemingly need to be aware of any non-standard ports (eg 44300).
The problem with that is that I'd be writing something to solve a problem that would only occur in a development environment.
So my question is... How do I change the port to 443 and have IIS Express like it?
Config for my site is below:
<site name="MySite" id="2" serverAutoStart="true">
<application path="/">
<virtualDirectory path="/" physicalPath="C:\Inetpub\MySite" />
</application>
<bindings>
<binding protocol="http" bindingInformation=":80:" />
<binding protocol="https" bindingInformation=":44300:" />
</bindings>
</site>
Many thanks in advance.
Update:
This question has been answered by Divya over on the IIS forums.
This question has been answered by Divya over on the IIS forums.
Once you enable SSL for a website in WebMatrix, it defaults to port 44300 and does all the bindings in the background. I am hoping that you tried to change this port to 443 in the config file. Once that is done and saved, you also need to modify the binding in http.sys. You would need to delete the existing entry for port 44300 and add the entry for port 443.
To do this, you could use httpcfg (WinXp/Win2003) or 'netsh http' (WinVista/Win2K8/Win7).
Here are the commands for netsh:
1) Get the appid and certhash for the existing entry of 44300 (I
assume, you are going to use the same certificate which WebMatrix
installs by default. If you want to change the certificate as well,
get the certificate hash of the certificate from the certificate
store): netsh http show sslcert. In the output search for entry for
port 44300 and copy certhash and appID.
2) Delete the entry for 44300: netsh http delete sslcert ipport=0.0.0.0:44300
3) Add a new entry for port 443 with certhash and appID copied in step
1. netsh http add sslcert ipport=0.0.0.0:443 certhash=<certhash> appid=<appid>
After configuring the entry in http.sys, you need to restart http
service for the changes to take effect.
net stop http
net start http
As noted by others, there are several nice ways of getting your SSL certs.
netsh http show sslcert > output.txt
or (my preferred method):
netsh http show sslcert | clip
Since I have spent much time on this topic , I would like to share my finding. I am reposting segment from my other post minus the code. Some background and explanation:
==========================================
After researching aroud, I was able to solve this issue with IIS Express and an override of the Controller class's OnAuthorization method (Ref#1). I have also gone with the route recommended by Hanselman (Ref#2). However, I was not complete satisfied with these two solutions due to two reasons:
Ref#1's OnAuthorization only works at the action level, not at the controller class level
Ref#2 requires a lot of setup (Win7 SDK for makecert), netsh commands, and, in order to use port 80 and port 443, I need to launch VS2010 as administrator, which I frown upon.
So, I came up with this solution that is quite simplistic with the following conditions:
I want to be able to use the RequireHttps attribute at Controller class or action level
I want MVC to use HTTPS when the RequireHttps attribute is present, and use HTTP if it is absent
I do not want to have to run Visual Studio as administrator
I want to be able to use any HTTP and HTTPS ports that are assigned by IIS Express
I can reuse the self-signed SSL cert of IIS Express, and I do not care if I see the invalid SSL Prompt
=========================================
You can find my solution/code here ==> ASP.NET MVC RequireHttps in Production Only
The port 44300 is sequential: 00 mean that its the first application you have configured as SSL enabled; 01 will be the second one and so on.
Since I also require my website to only work in HTTPS by adding the [RequireHttps] global attribute, I had some trouble debugging. When launched, it was automatically redirecting to https://localhost/
To fix this problem when debugging a web site, I simply create a new RequireHttpsAttribute that specify the port
#if DEBUG
public class RequireHttpsAttribute : System.Web.Mvc.RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
base.HandleNonHttpsRequest(filterContext);
var result = (RedirectResult)filterContext.Result;
var uri = new UriBuilder(result.Url);
uri.Port = 44301;
filterContext.Result = new RedirectResult(uri.ToString());
}
}
#endif
Use this class when debugging only. When deployed to IIS7, you should use Url rewriting to redirect to HTTPS.
Dan answer is right but if you still have problems with configuring IIS Express to serve your website with http and https on standard ports here is nice tutorial that that guide you step by step:
http://www.lansweeper.com/kb/54/How-to-configure-SSL-in-IIS-Express.html
In my case I accidentally deleted IIS Express certificate. I think it is generated the first time you use SSL in Visual Studio (F4 on selected project to get properties window and checking 'SSS Enabled' checkbox). This tutorial guided me how to create certificate and fix it.
Create class
public class RequireSSLAttribute: RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
base.HandleNonHttpsRequest(filterContext);
if (filterContext.HttpContext.Request.Url.Host.ToLower().Equals("localhost"))
{
// redirect to HTTPS version of page
string localhostSSLPort = System.Configuration.ConfigurationManager.AppSettings["localhostSSLPort"];
string url = "https://" + filterContext.HttpContext.Request.Url.Host + ":" + localhostSSLPort + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
}
}
And inside your web config add something like this
<appSettings>
<add key="localhostSSLPort" value="44300"/>
</appSettings>
And then you use it like
[RequireSSL]
public class AdminController : Controller
{
...
}

Resources