impersonation in Exchange Web Service (EWS) - impersonation

I am developing a application for create appointments in my calendar and sending mails on our company's exchange 2013 server using the asp.net 4.0 web application . I get the error as follows:
Unable to cast object of type 'System.Security.Principal.GenericIdentity' to type 'System.Security.Principal.WindowsIdentity'.
My LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) function always return 0.
i am Using service.UseDefaultCredentials = true; I just cannot afford to use the username/paasword for every staff who will be using this application. I am thinking there is some problem (rights/permissions/disabled impersonation) issue at the production Web application server (Windows 2008 m/c).
These are the code files:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Exchange.WebServices.Autodiscover;
using System.Web.Configuration;
//for impersonation before making calls
using System.Security.Principal;
using System.Web.Security;
using System.Runtime.InteropServices;
namespace MvcApplication1
{
public partial class Test1 : System.Web.UI.Page
{
protected ExchangeService service;
//The following Impersonator*** variables are of the exchange account which has been configured to impersonate other users by enabling impersonation on the exchange server as they show at this link: http://msdn.microsoft.com/en-us/library/office/bb204095(v=exchg.140).aspx
protected string ImpersonatorUsername = WebConfigurationManager.AppSettings["ImpersonatorUsername"];
protected string ImpersonatorPassword =WebConfigurationManager.AppSettings["ImpersonatorPassword"];
protected string ImpersonatorDomain = WebConfigurationManager.AppSettings["ImpersonatorDomain"];
// This is for the user for whom the appointment need to be set on their exchange server. This user will be impersonated by the above impersonator. You do not need to get the password information for this user, just the email address will work.
private string Username ="mtyagi#talygen.local";// HttpContext.Current.User.Identity.Name.Split('\\').Last(); //extract the username out of the "Domain\Username" format. It doesn't have to be the currently logged in user. As per your need you can use the username of any other company user for whom you know the email address.
protected string ImpersonatedEmailAddress;//= Username +"#"+ WebConfigurationManager.AppSettings["EmailDomain"];
//start impersonation setup block. Credits: Impersonate a Specific User in Code http://support.microsoft.com/kb/306158#4
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
//end impersonation setup block;
protected void Page_Load(object sender, EventArgs e)
{
ImpersonatedEmailAddress = Username;// + "#" + WebConfigurationManager.AppSettings["EmailDomain"]; //form the email address out of the username, provided they both are same
service = new ExchangeService(ExchangeVersion.Exchange2013);
//service.UseDefaultCredentials = true;
service.Credentials = new WebCredentials(ImpersonatorUsername, ImpersonatorPassword, ImpersonatorDomain);
service.Url = new Uri("https://exchange.talygen.com/EWS/exchange.asmx");//new Uri(WebConfigurationManager.AppSettings["EWSURL"]);
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, ImpersonatedEmailAddress);
SetAppointment("Test", DateTime.Now, "Test");
}
public void SetAppointment(string Subject, DateTime AptDateTime, string Body)
{
Appointment apt = new Appointment(service);
apt.Subject = Subject;
apt.Body = Body;
apt.Body.BodyType = BodyType.HTML;
apt.Start = AptDateTime;
apt.End = apt.Start.AddMinutes(30.00);
apt.ReminderMinutesBeforeStart = 15;
apt.IsReminderSet = true;
if (impersonateValidUser(ImpersonatorUsername, ImpersonatorDomain, ImpersonatorPassword)) //For this code to work you will have to enable impersonation on the Exchange server. This code works on the web application running on the company server, but not from my XP PC that is not part of the domain but is on VPN connection.
{
HttpContext.Current.Trace.Write("Before Saving Appointment. System.Security.Principal.WindowsIdentity.GetCurrent().Name = " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
apt.Save(SendInvitationsMode.SendToNone);
HttpContext.Current.Trace.Write("After Saving Appointment.");
Label1.Text = String.Format("Appointment set successfully for {0}", ImpersonatedEmailAddress);
}
else //fall back to the code that uses logged in user's window identity and not impersonation. This code "strangely" worked from the web application installed on my Windows XP PC that was not part of the domain but was on VPN connection and yet saved appointments on the company's exchange server. I guess, the VPN connection compensates for all the mumbo-jumbo round about impersonation code in the impersonateValidUser method. Hack, this code worked even I had not configured the impersonation on the exchange server as they tell you to do at this link: http://msdn.microsoft.com/en-us/library/office/bb204095(v=exchg.140).aspx
{
service.Credentials = null;
service.ImpersonatedUserId = null;
service.UseDefaultCredentials = true;
HttpContext.Current.Trace.Write("Before Impersonation: System.Security.Principal.WindowsIdentity.GetCurrent().Name = " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
//this is not impersonation. It uses the logged in user's window identity. The window identity does not have to be that of the company domain. The windows identity of Local PC that is not part of the domain will also work
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext = ((System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity).Impersonate();// //System.Threading.Thread.CurrentPrincipal.Identity
HttpContext.Current.Trace.Write("Before Saving Appointment. System.Security.Principal.WindowsIdentity.GetCurrent().Name = " + System.Security.Principal.WindowsIdentity.GetCurrent().Name);
apt.Save(SendInvitationsMode.SendToNone);
impersonationContext.Undo();
}
}
//impersonation methods. Credit: Impersonate a Specific User in Code: http://support.microsoft.com/kb/306158#4
private bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
private void undoImpersonation()
{
impersonationContext.Undo();
}
}
}
And my web config code is as bellow:
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />`enter code here
</configSections>
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MvcApplication1-20140210213058;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-MvcApplication1-20140210213058.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="webpages:Version" value="2.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="PreserveLoginUrl" value="true" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<!--<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>-->
<authentication mode="Windows" />
<identity impersonate="true" />
<pages>
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages" />
</namespaces>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
</entityFramework>
</configuration>
Please help me in short out this problem.
Thanks Mukesh

Your asp.net 4.0 web application needs to run under a service account that has Exchange impersonation rights for the mailbox users. Assuming everyone is in the same Exchange organization, you'll need to run the New-ManagementRoleAssignment cmdlet to grant impersonation privileges for your service account.
Then you'll need to do impersonation just like you have in your example. You won't use Windows impersonation.

Related

ServiceStack with Razor - Model is null (deployed to Azure)

When using ServiceStack / Razor, I'm having issues rendering a view in Azure only.
http://nehcr-dev.azurewebsites.net/with-view
Here is a screenshot of the page locally:
!https://www.dropbox.com/s/up5bmixdjmcc9bi/with-view.png?dl=0
Here is my service response:
return new HttpResult(new IndexResponse("Home | Northeast Healthcare Recruitment")
{
TotalJobs = metadata.TotalJobs,
GroupedJobTypeCounts = groupedJobTypeCounts,
MostRecentJobs = new List<JobListingSummary>()
})
{
View = "Index"
};
where IndexResponse is
public class IndexResponse : BaseResponse
{
public IndexResponse(string title) : base(title) { }
public int TotalJobs { get; set; }
public List<List<JobTypeCount>> GroupedJobTypeCounts { get; set; }
public List<JobListingSummary> MostRecentJobs { get; set; }
}
and BaseResponse is
public abstract class BaseResponse
{
public BaseResponse(string title)
{
if (string.IsNullOrWhiteSpace(title))
{
Title = "Northeast Healthcare Recruitment";
}
Title = title;
}
public string Title { get; set; }
}
the beginning of my view looks like:
#inherits ViewPage<NEHCR.ServiceModel.IndexResponse>
#section head{
}
<section id="content" class="nehcr-content">
<!-- JOB SEARCH START -->
<section class="nehcr-search-hero">
<div class="container">
<div>
<div class="nehcr-search-text">
<h1 class="nehcr-search-text-header">Search from #Model.TotalJobsĀ job offers</h1>
<p class="nehcr-search-text-p">Your career starts here.</p>
</div>
<form method="GET" action="/jobs" class="nehcr-search-wrap nehcr-row">
and my web.config is:
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false"/>
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false"/>
</sectionGroup>
</configSections>
<appSettings>
<add key="webPages:Enabled" value="false" />
<add key="ConnectionString" value="" />
</appSettings>
<connectionStrings>
<add name="ConnectionString" connectionString="" providerName="System.Data.SqlClient" />
</connectionStrings>
<!--
For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.
The following attributes can be set on the <httpRuntime> tag.
<system.Web>
<httpRuntime targetFramework="4.6.2" />
</system.Web>
-->
<system.web>
<compilation debug="true" targetFramework="4.6.2">
<buildProviders>
<add extension=".cshtml" type="ServiceStack.Razor.CSharpRazorBuildProvider, ServiceStack.Razor"/>
</buildProviders>
</compilation>
<httpRuntime targetFramework="4.5"/>
<httpHandlers>
<add path="*" type="ServiceStack.HttpHandlerFactory, ServiceStack" verb="*"/>
</httpHandlers>
<customErrors mode="Off">
</customErrors>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<urlCompression doStaticCompression="true" doDynamicCompression="false"/>
<handlers>
<add path="*" name="ServiceStack.Factory" type="ServiceStack.HttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true"/>
</handlers>
</system.webServer>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<pages pageBaseType="ServiceStack.Razor.ViewPage">
<namespaces>
<add namespace="System"/>
<add namespace="System.Linq"/>
<add namespace="ServiceStack"/>
<add namespace="ServiceStack.Html"/>
<add namespace="ServiceStack.Razor"/>
<add namespace="ServiceStack.Text"/>
<add namespace="ServiceStack.OrmLite"/>
<add namespace="NEHCR"/>
<add namespace="NEHCR.ServiceModel"/>
</namespaces>
</pages>
</system.web.webPages.razor>
Locally it runs fine, IISExpress on Win10. Deployed to Azure I get this:
System.NullReferenceException occurred
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=nmhpfpfd
StackTrace:
at ASP.Views.Home.__Index.Execute() in d:\home\site\wwwroot\Views\Home\Index.cshtml:line 15
at ServiceStack.Razor.ViewPage`1.WriteTo(StreamWriter writer)
at ServiceStack.Razor.Managers.RazorPageResolver.ExecuteRazorPageWithLayout(RazorPage razorPage, IRequest httpReq, IResponse httpRes, Object model, IRazorView pageInstance, Func`1 layout)
at ServiceStack.Razor.Managers.RazorPageResolver.ExecuteRazorPage(IRequest httpReq, IResponse httpRes, Object model, RazorPage razorPage)
at ServiceStack.Razor.Managers.RazorPageResolver.ProcessRequest(IRequest httpReq, IResponse httpRes, Object dto)
at ServiceStack.Formats.HtmlFormat.<>c__DisplayClass10_0.<SerializeToStream>b__0(IViewEngine x)
at ServiceStack.Formats.HtmlFormat.SerializeToStream(IRequest req, Object response, IResponse res)
ServiceStack.Razor.ViewPageBase<TModel>.Model.get returned null.
If I change my service reponse to:
return new IndexResponse("Home | Northeast Healthcare Recruitment")
{
TotalJobs = metadata.TotalJobs,
GroupedJobTypeCounts = groupedJobTypeCounts,
MostRecentJobs = new List<JobListingSummary>()
};
it works fine: http://nehcr-dev.azurewebsites.net/
Thanks for any insight.

NLog is not writing to database table

This is my NLog Config file.
<?xml version="1.0" ?>
<nlog autoReload="true" throwExceptions="true" internalLogLevel="Debug" internalLogToConsole="true"
internalLogFile="c:\\temp\\nlog.txt"
>
<targets>
<!--Useful for debugging-->
<target name="consolelog" type="ColoredConsole"
layout="${date:format=HH\:mm\:ss}|${level}|${stacktrace}|${message}" />
<target name="filelog" type="File" fileName="c:\\temp\\nlog1.txt"
layout="${date}: ${message}" />
<target name="eventlog" type="EventLog" source="My App" log="Application"
layout="${date}: ${message} ${stacktrace}" />
<target name="databaselog" type="Database">
<dbProvider>sqlserver</dbProvider>
<!-- database connection parameters -->
<!-- alternatively you could provide a single 'connectionstring' parameter -->
<connectionString>Data Source=.\SQLEXPRESS;Initial Catalog=SnSolutions;User Id=sa;Password=test#1234#;</connectionString>
<!--<add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=MyTest;User Id=mytest;Password=mytest123;" providerName="System.Data.SqlClient" />-->
<commandText>
insert into NLog_Error ([time_stamp],[level],[host],[type],1,[logger],[message],[stacktrace],[allxml]) values(#time_stamp,#level,#host,#type,#source,#logger,#message,#stacktrace,#allxml);
</commandText>
<parameter name="#time_stamp" layout="${date}" />
<parameter name="#level" layout="${level}" />
<parameter name="#host" layout="${machinename}" />
<parameter name="#type" layout="${exception:format=type}" />
<parameter name="#source" layout="${callsite:className=true:fileName=false:includeSourcePath=false:methodName=false}" />
<parameter name="#logger" layout="${logger}" />
<parameter name="#message" layout="${message}" />
<parameter name="#stacktrace" layout="${exception:stacktrace}" />
<parameter name="#allxml" layout="${web_variables}" />
</target>
</targets>
<rules>
<!--
<logger name="*" minlevel="Fatal" writeTo="eventlog" />
-->
<logger name="*" minlevel="Info" writeTo="filelog" />
<logger name="*" minlevel="Info" writeTo="databaselog" />
</rules>
</nlog>
This is my NLogLogger Class
public class NLogLogger
{
private readonly Logger _logger;
public NLogLogger(Logger logger)
{
_logger = logger;
}
public NLogLogger()
{
StackFrame frame = new StackFrame(1, false);
_logger = LogManager.GetLogger(frame.GetMethod().DeclaringType.FullName);
}
public void Trace(string message)
{
_logger.Trace(message);
}
public void Debug(string message)
{
_logger.Debug(message);
}
public void Info(string message)
{
_logger.Info(message);
}
public void Warn(string message)
{
_logger.Warn(message);
}
public void Error(string message)
{
_logger.Error(message);
}
public void Fatal(string message)
{
_logger.Fatal(message);
}
}
which I am trying to use in the following way.
NLogLogger logger = new NLogLogger();
logger.Info("We're on the Index page for Activities");
But not able to see any records in the DB nor any error in the File System.
Please let me know which is the part I am missing.
Thanks in advance.
You have an error in insert command - 1 is not a valid column name.
insert into NLog_Error (...[type],1,[logger]..)
Here is an example regarding logging to DB.

Enable GZip compression for SVG in Azure Web Sites?

I'm trying to enable GZip compress for SVG in an Azure Web Site using web.config transforms without success. Here is what my transform looks like:
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.webServer>
<httpCompression>
<staticTypes>
<add mimeType="image/svg+xml" enabled="true" xdt:Transform="Insert" />
</staticTypes>
</httpCompression>
<staticContent xdt:Transform="Insert">
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
</staticContent>
</system.webServer>
</configuration>
This should both add the mime type for SVG, which Azure doesn't seem to have, and then enable compression. I've verified the mime type addition works fine, but upon publishing I get an error for the compression elements:
No element in the source document matches
'/configuration/system.webServer/httpCompression/staticTypes'
Removing the compression from the transform and adding it directly to my web.config file removes the error, but I still don't see the compression in the HTTP headers. Here are the response headers:
Accept-Ranges:bytes
Content-Length:23265
Content-Type:image/svg+xml
Date:Mon, 10 Jun 2013 17:19:37 GMT
ETag:"c4e9ec93d765ce1:0"
Last-Modified:Mon, 10 Jun 2013 12:39:41 GMT
Server:Microsoft-IIS/8.0
X-Powered-By:ASP.NET
X-Powered-By:ARR/2.5
X-Powered-By:ASP.NET
Here is how you can enable it in your web.config:
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
</staticContent>
<httpCompression>
<staticTypes>
<remove mimeType="*/*" />
<add mimeType="image/svg+xml" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
</system.webServer>
</configuration>
The key line is the removal of the catch-all (and later re-add). If you don't have that, then the svg line basically gets ignored since the catch-all is inherited from applicationhost.config, and catches all before it reaches svg line.
Unfortunately it isn't possible to use built-in http compression on Azure Websites for image/xml+svg mime types. You have to change some IIS settings to do that which is possible if you're using Azure Web Roles.
I didn't want to go through that hassle however so I just made a controller in MVC to handle .svg files.
[AttributeRouting.RoutePrefix("static")]
public class ContentController : Controller
{
[GET(#"fonts/{fileName:regex(^[\w-\.]+\.svg$)}")]
[Compress, OutputCache(
Duration = 3600 * 24 * 30,
Location = OutputCacheLocation.Any,
VaryByContentEncoding = "gzip;deflate",
VaryByParam = "fileName")]
public ActionResult SvgFont(string fileName)
{
var path = Server.MapPath("~/Content/fonts/" + fileName);
if (!System.IO.File.Exists(path)) return HttpNotFound();
return File(path, "image/svg+xml");
}
}
public class CompressAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.CompressResult();
}
}
public static class HttpContextExtensions
{
public static bool CompressResult(this HttpContextBase context)
{
var request = context.Request;
var response = context.Response;
if (request == null || response == null) return false;
var filter = response.Filter;
if (filter is GZipStream || filter is DeflateStream) return false;
var acceptEncoding = (request.Headers["Accept-Encoding"] ?? string.Empty).ToLowerInvariant();
if (acceptEncoding.Contains("gzip"))
{
response.Filter = new GZipStream(filter, CompressionMode.Compress);
response.AddHeader("Content-Encoding", "gzip");
response.AppendHeader("Vary", "Content-Encoding");
return true;
}
if (acceptEncoding.Contains("deflate"))
{
response.Filter = new DeflateStream(filter, CompressionMode.Compress);
response.AddHeader("Content-Encoding", "deflate");
response.AppendHeader("Vary", "Content-Encoding");
return true;
}
return false;
}
}
You will also need to add this to your Web.config file so that MVC will handle routes with a .svg extension
<system.webServer>
<handlers>
<add name="StaticMvcHandler" path="static/fonts/*.svg" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
I have the following configuration entries for an Azure Web-Site:
<system.webServer>
<urlCompression doStaticCompression="true" doDynamicCompression="true" />
</system.webServer>
and
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
<!-- Scalable Vector Graphics iPhone, iPad -->
<mimeMap fileExtension=".svgz" mimeType="image/svg+xml" />
I have added the .svgz extension as well (for compressed svg).
The above solution worked for me but I first had to remove the file extension. After that I got the results I was looking for.
<staticContent>
<remove fileExtension=".svg" />
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
</staticContent>

WebAPI DELETE request return 404 error in Azure

I have been trying to get the DELETE requests in my ASP.net WebAPI(ASP.net web role) working on Azure and locally on my machine.But end up getting "not found" error.
I have following configuration for the web.config of the ASP.net WebAPI web role:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules>
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit"
path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
modules="IsapiModule"
scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll"
preCondition="classicMode,runtimeVersionv4.0,bitness32"
responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit"
path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
modules="IsapiModule"
scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll"
preCondition="classicMode,runtimeVersionv4.0,bitness64"
responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0"
path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
For making it work on IISExpress, I changed the $:\Users\\Documents\IISExpress\config\applicationhost.config configuration and commented out the WebDav portions of it and added the "PUT,DELETE" verbs appropriately.
To test my WebAPI I created a simple windows forms (.net 4.5 ) c# application and used the httpclient as below:
const string key = "user1";
var client = new HttpClient { BaseAddress = new Uri("http://abcd.cloudapp.net/") };
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var requestUrl = string.Format("api/user/{0}", key);
var deleteAsync = client.DeleteAsync(requestUrl).Result;
The response I get it following:
{StatusCode: 404,
ReasonPhrase: 'Not Found',
Version: 1.1,
Content: System.Net.Http.StreamContent,
Headers:
{
Pragma: no-cache
Cache-Control: no-cache
Date: Fri, 12 Apr 2013 06:40:31 GMT
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 128
Content-Type: application/json; charset=utf-8
Expires: -1
}} System.Net.Http.HttpResponseMessage
How do I get it to work on Azure? and possibly on my local machine as well.

wad-iis-failedreqlogfiles can't be found in my blob

I use this web.config:
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Security" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="200-999" />
And in webrole.cs
public override bool OnStart()
{
//Get Default Config
DiagnosticMonitorConfiguration config = DiagnosticMonitor.GetDefaultInitialConfiguration();
//IIS Logs
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
Trace.WriteLine("WAD Monitor started", "Information");
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", config);
RoleEnvironment.Changing += RoleEnvironmentChanging;
return base.OnStart();
}
I can get wad-iis-logsfiles blob, but I can't get wad-iis-failedreqlogfiles blob on my emulator
Why since 200-999 include all request!
And should have a log files.
Well, what I did is to set up my settings in the WebRole file and the code that I added to my web.config are this configurations
<system.diagnostics>
<trace>
<listeners>
<add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="AzureDiagnostics">
<filter type="" />
</add>
</listeners>
</trace>
</system.diagnostics>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module" verbosity="Verbose" />
</traceAreas>
<failureDefinitions verbosity="Warning" statusCodes="400-599" />
</add>
</traceFailedRequests>
</tracing>
</system.webServer>
And then I implement the method Onstart with this configurations.
public override bool OnStart()
{
String wadConnectionString = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString";
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue(wadConnectionString));
RoleInstanceDiagnosticManager roleInstanceDiagnosticManager = cloudStorageAccount.CreateRoleInstanceDiagnosticManager(
RoleEnvironment.DeploymentId,
RoleEnvironment.CurrentRoleInstance.Role.Name,
RoleEnvironment.CurrentRoleInstance.Id);
DiagnosticMonitorConfiguration config = roleInstanceDiagnosticManager.GetCurrentConfiguration();
//Add Events
config.WindowsEventLog.DataSources.Add("System!*");
config.WindowsEventLog.DataSources.Add("Application!*");
config.WindowsEventLog.ScheduledTransferLogLevelFilter = LogLevel.Error;
config.WindowsEventLog.ScheduledTransferPeriod =TimeSpan.FromSeconds(15.0);
config.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
config.Logs.ScheduledTransferPeriod = TimeSpan.FromSeconds(15.0);
//transfer the IIS and IIS Failed Request Logs
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1.0);
roleInstanceDiagnosticManager.SetCurrentConfiguration(config);
return base.OnStart();
}
And also I recommend you to check your ConnectionString on your WerRole settings, it should look something like this:
"DefaultEndpointsProtocol=http;AccountName=myAccount;AccountKey=8zTMPlQ8N76cEUNGLYhIvPf8lDmmTnCm7BICX/xtPmdr9vN7elOvZS5N2njtg+tbStoCoe30doN0sCrE1LHcsd=="
or
"UseDevelopmentStorage=true"
If you want to work on your development enviroment
Also you can take a look to this site for more details
http://robindotnet.wordpress.com/2011/02/16/azure-toolssdk-1-3-and-iis-logging/
Finally I found the answer!
This is because IIS applicationHost auto disable the trace log file.
When I add this code to webrole.cs and input a invalid url it works!
using (ServerManager serverManager = new ServerManager())
{
Configuration iisConfig = serverManager.GetApplicationHostConfiguration();
ConfigurationSection sitesSection = iisConfig.GetSection("system.applicationHost/sites");
ConfigurationElement siteDefaultsElement = sitesSection.GetChildElement("siteDefaults");
ConfigurationElement logFileElement = siteDefaultsElement.GetChildElement("logFile");
logFileElement["enabled"] = true;
serverManager.CommitChanges();
}

Resources