Application Insights RequestTelemetry not showing up in Requests after exception - asp.net-core

I have spent a while trying to RequestTelemetry to work. It did when I was first playing around with it, but then oddly just stopped working whenever an exception is thrown. I have read documentation using Application Insights for custom events and metrics as well as Custom Operations Tracking and tried to add all of the best practices to see if I could get the result to show up again. I'm using .NET Core 3.1 and Microsoft.ApplicationInsights.AspNetCore 2.14.0.
Setup for the Webapp looks like this in Startup.cs
services.AddApplicationInsightsTelemetry(new ApplicationInsightsServiceOptions {
EnableAdaptiveSampling = false
});
I have the telemetry inside of a Controller Post Action. I realize that Application Insights is already tracking it the post action, but I wanted to see if I could track the inner method. This is the code in my controller:
public MyController(IMyService myService, TelemetryClient telemetryClient, ILogger<MyController> logger) {
_myService = myService;
_telemetryClient = telemetryClient;
_logger = logger;
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> PostAsync([FromBody] MyModel model) {
using var scope = _logger.BeginScope(new Dictionary<string, object> {
{ $"{nameof(PostAsync)}.Scope", Guid.NewGuid() },
{ nameof(model.Name), model.Name }
});
model.AuthenticatedUserId = User.GetUserIdFromClaims();
var requestTelemetry = new RequestTelemetry { Name = nameof( _myService.MyFunctionAsync) };
var operation = _telemetryClient.StartOperation(requestTelemetry);
operation.Telemetry.Properties.Add("User", model.AuthenticatedUserId);
try {
await _myService.MyFunctionAsync(model).ConfigureAwait(false); // <-- throws exception
operation.Telemetry.Success = true;
return NoContent();
} catch (Exception e) {
operation.Telemetry.Success = false;
throw;
} finally {
_telemetryClient.StopOperation(operation);
}
}
I can see in the Visual Studio console output that the code executes, as I get the following log, but it never shows up in the Application Insights Requests.
Application Insights Telemetry: {
"name": "AppRequests",
"time": "2020-06-21T14:29:08.7469588Z",
"iKey": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"tags": {
"ai.application.ver": "1.0.0.0",
"ai.cloud.roleInstance": "DESKTOP-K74PNCU",
"ai.operation.id": "0443259d660125498cf28f8f7a275dab",
"ai.operation.parentId": "1dea6f9b27220c4c",
"ai.operation.name": "POST EventEmitter/Post",
"ai.location.ip": "::1",
"ai.internal.sdkVersion": "dotnetc:2.14.0-17971",
"ai.internal.nodeName": "DESKTOP-K74PNCU"
},
"data": {
"baseType": "RequestData",
"baseData": {
"ver": 2,
"id": "2b7900eedfb7c34d",
"name": "MyFunctionAsync",
"duration": "00:00:00.3766937",
"success": false,
"properties": {
"DeveloperMode": "true",
"User": "pobl-dev",
"_MS.ProcessedByMetricExtractors": "(Name:'Requests', Ver:'1.1')",
"AspNetCoreEnvironment": "Development"
}
}
}
}

There is a simple solution, but I'm not sure of why it's necessary, due to either a lack in documentation or a bug. I found once a responseCode was provided everything works fine. There is a default responseCode of 200 which shows up on a successful call. Once I set the value on a failure everything worked fine.
public MyController(IMyService myService, TelemetryClient telemetryClient, ILogger<MyController> logger) {
_myService = myService;
_telemetryClient = telemetryClient;
_logger = logger;
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> PostAsync([FromBody] MyModel model) {
using var scope = _logger.BeginScope(new Dictionary<string, object> {
{ $"{nameof(PostAsync)}.Scope", Guid.NewGuid() },
{ nameof(model.Name), model.Name }
});
model.AuthenticatedUserId = User.GetUserIdFromClaims();
var requestTelemetry = new RequestTelemetry { Name = nameof( _myService.MyFunctionAsync) };
var operation = _telemetryClient.StartOperation(requestTelemetry);
operation.Telemetry.Properties.Add("User", model.AuthenticatedUserId);
try {
await _myService.MyFunctionAsync(model).ConfigureAwait(false); // <-- throws exception
operation.Telemetry.Success = true;
operation.Telemetry.ResponseCode = "Roses";
return NoContent();
} catch (Exception e) {
operation.Telemetry.Success = false;
operation.Telemetry.ResponseCode = "Funky"; // <-- seems to be required on a failure
throw;
} finally {
_telemetryClient.StopOperation(operation);
}
}

This is to add some context to the accepted answer if you're curious:
Here's the source code for RequestTelemetry
When it prepares the data to send to Azure servers it explicitly elects NOT to set a default response code unless success == true in which case the default is 200.
// Required fields
if (!this.Success.HasValue)
{
this.Success = true;
}
if (string.IsNullOrEmpty(this.ResponseCode))
{
this.ResponseCode = this.Success.Value ? "200" : string.Empty;
}
If you run a simple Kusto query on the logs:
union requests
| where timestamp > ago(1hr)
| where customDimensions["CustomOperationCategory"] in ("Identity")
| take 100
You'll only see unsuccessful results where you did set a status code:
I don't know if something ever changed, but Microsoft's examples sometimes do the same.

Related

Getting shared calendar events with Microsoft.Graph

I'm trying to recreate the following example: https://learn.microsoft.com/en-us/graph/outlook-get-shared-events-calendars
I'm using ASP.NET Core 3.1, Azure AD, and the Microsoft.Graph API. I was able to retrieve events from my personal calendar, as well as the name, ID, owner etc. from the shared calendars. The events from the shared calendars, however, refuse to show. I couldn't find any examples, Microsoft's or otherwise.
Here's the code I'm using:
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "MicrosoftGraph:Scopes")]
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly GraphServiceClient graphServiceClient;
private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;
private string[] _graphScopes;
public HomeController(ILogger<HomeController> logger, GraphServiceClient gsc,
MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler
)
{
_logger = logger;
graphServiceClient = gsc;
this._consentHandler = consentHandler;
_graphScopes = Startup.ConfigurationInstance.GetSection("MicrosoftGraph")["Scopes"].Split(" ");
}
[AuthorizeForScopes(ScopeKeySection = "MicrosoftGraph:Scopes")]
public IActionResult Index()
{
User currentUser = null;
try
{
currentUser = graphServiceClient.Me.Request().GetAsync().Result;
}
// Catch CAE exception from Graph SDK
catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge"))
{
try
{
Console.WriteLine($"{svcex}");
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
_consentHandler.ChallengeUser(_graphScopes, claimChallenge);
return new EmptyResult();
}
catch (Exception ex2)
{
_consentHandler.HandleException(ex2);
}
}
var viewOptions = new List<QueryOption>
{
new QueryOption("startDateTime", DateTime.Today.ToString("o")),
new QueryOption("endDateTime", DateTime.Today.AddDays(20).ToString("o"))
};
var sha = graphServiceClient.Me.Calendars
.Request()
.Select(e => e.Id)
.GetAsync().Result.ToList();
var x = new List<Event>();
foreach(var row in sha)
{
var id = row.Id;
var data = graphServiceClient.Me.Calendars[id].Request(viewOptions)
.Select(e => e.Events).GetAsync().Result;
ICalendarEventsCollectionPage d1 = data != null? data.Events: null;
if (d1 != null)
{
x.AddRange(d1.ToList());
}
}
}
}
The above retrieves null events.
I tried to access the calendar directly from another user, as per another one of Microsoft's examples:
var x = graphServiceClient.Users["<email>"]
.CalendarView
.Request(viewOptions)
.Select(e => new
{
e.Subject,
e.Organizer,
e.Start,
e.End
}).GetAsync().Result;
The only thing I get from this code is 'access denied'.
My appsettings.json with the scopes set in the Azure AD tenant:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<domain>",
"TenantId": "<id>",
"ClientId": "<id>",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc",
"ClientSecret": "<secret>"
},
"AllowedHosts": "*",
"MicrosoftGraph": {
"Scopes": "User.Read MailboxSettings.ReadWrite Calendars.ReadWrite.Shared",
"BaseUrl": "https://graph.microsoft.com/v1.0"
}
Any help with this would be greatly appreciated.
Your code and configuration don't seem to be the problem. Please check whether the Permissions to allow access is enabled in Outlook's calendar.
If the problem persists, here is a detailed documentation to guide you through the configuration step by step. You can refer to this documentation to see if some steps are missing.
Hop this can help you.

Using MQTT ManagedClient with ASP NET API, how to?

I'm currently working on a project that has to rely heavily on MQTT - one of the parts that needs to utilize MQTT is a ASP Net API, but I'm having difficulties receiving messages.
Here is my MQTTHandler:
public MQTTHandler()
{
_mqttUrl = Properties.Resources.mqttURL ?? "";
_mqttPort = Properties.Resources.mqttPort ?? "";
_mqttUsername = Properties.Resources.mqttUsername ?? "";
_mqttPassword = Properties.Resources.mqttUsername ?? "";
_mqttFactory = new MqttFactory();
_tls = false;
}
public async Task<IManagedMqttClient> ConnectClientAsync()
{
var clientID = Guid.NewGuid().ToString();
var messageBuilder = new MqttClientOptionsBuilder()
.WithClientId(clientID)
.WithCredentials(_mqttUsername, _mqttPassword)
.WithTcpServer(_mqttUrl, Convert.ToInt32(_mqttPort));
var options = _tls ? messageBuilder.WithTls().Build() : messageBuilder.Build();
var managedOptions = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
.WithClientOptions(options)
.Build();
_mqttClient = new MqttFactory().CreateManagedMqttClient();
await _mqttClient.StartAsync(managedOptions);
Console.WriteLine("Klient startet");
return _mqttClient;
}
public async Task PublishAsync(string topic, string payload, bool retainFlag = true, int qos = 1)
{
await _mqttClient.EnqueueAsync(new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel((MQTTnet.Protocol.MqttQualityOfServiceLevel)qos)
.WithRetainFlag(retainFlag)
.Build());
Console.WriteLine("Besked published");
}
public async Task SubscribeAsync(string topic, int qos = 1)
{
var topicFilters = new List<MQTTnet.Packets.MqttTopicFilter>
{
new MqttTopicFilterBuilder()
.WithTopic(topic)
.WithQualityOfServiceLevel((MQTTnet.Protocol.MqttQualityOfServiceLevel)(qos))
.Build()
};
await _mqttClient.SubscribeAsync(topicFilters);
}
public Status GetSystemStatus(MqttApplicationMessageReceivedEventArgs e)
{
try
{
var json = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
var status = JsonSerializer.Deserialize<Status>(json);
if (status != null)
{
return status;
}
else
{
return null;
}
}
catch (Exception)
{
throw;
}
}
The above has been tested with a console app and works as it should.
The reason I need MQTT in the APi is that a POST method has to act on the value of a topic;
In particular I need to check a systems status before allowing the post;
[HttpPost]
public async Task<ActionResult<Order>> PostOrder(Order order)
{
if (_lastStatus != null)
{
if (_lastStatus.OpStatus)
{
return StatusCode(400, "System is busy!");
}
else
{
var response = await _orderManager.AddOrder(order);
return StatusCode(response.StatusCode, response.Message);
}
}
return StatusCode(400, "Something went wrong");
}
So I will need to set up a subscriber for this controller, and set the value of _lastStatus on received messages:
private readonly MQTTHandler _mqttHandler;
private IManagedMqttClient _mqttClient;
private Status _lastStatus;
public OrdersController(OrderManager orderManager)
{
_orderManager = orderManager;
_mqttHandler = new MQTTHandler();
_mqttClient = _mqttHandler.ConnectClientAsync().Result;
_mqttHandler.SubscribeAsync("JSON/Status");
_mqttClient.ApplicationMessageReceivedAsync += e =>
{
_lastStatus = _mqttHandler.GetSystemStatus(e);
return Task.CompletedTask;
};
}
However, it's behaving a little odd and I'm not experienced enough to know why.
The first time I make a POST request, _lastStatus is null - every following POST request seem to have the last retained message.
I'm guessing that I am struggling due to stuff being asynchronous, but not sure, and every attempt I've attempted to make it synchronous have failed.
Anyone have a clue about what I'm doing wrong?

Accessing HttpContext.Session from static method

I am getting following error when accessing HttpContext.Session from static method placed in separate task:
Session has not been configured for this application or request.
I used this article to implement access to HttpContext outside the controller
From controller I invoke this static method that used to retrieve image data:
public static void CreateDummyGallery(Gallery gallery)
{
Logger.LogDebug(LogModule.Dummy, $"Starting gallery creation.");
Task.Factory.StartNew(() =>
{
try
{
List<DummyPicture> pictures;
using (var context = new MyzeumContext())
{
int top = 10;
pictures = context.DummyPictures.FromSql($"SELECT * FROM dummypictures ORDER BY RAND() LIMIT {top}").ToList();
}
Logger.LogDebug(LogModule.Dummy, $"Starting retrieving images.");
Parallel.ForEach(pictures, picture => {
using (WebClient client = new WebClient())
{
}
});
Logger.LogDebug(LogModule.Dummy, $"Done retrieving images.");
}
catch(Exception e)
{
Logger.LogError(LogModule.Server, e.Message, e);
}
});
}
The problem occurs in Logger.LogDebug() because this is where I access HttpContext:
public void LogDebug(LogModule module, string message, Exception stackTrace = null)
{
Log record = new Log();
record.Module = module;
record.ThreadId = Environment.CurrentManagedThreadId;
record.SessionId = HttpContextHelper.Current?.Session?.Id;
record.Message = message;
record.Logged = DateTime.UtcNow;
if(stackTrace != null)
{
record.Message += $" :{stackTrace.StackTrace}";
}
queue.Enqueue(record);
}
The problem 99% occurs in the first call inside task:
Logger.LogDebug(LogModule.Dummy, $"Starting retrieving images.");
BUT, right after application starts this whole task block works fine and does not throw any exception. Problem starts after following requests.

masstransit with rabbitmq: Why message auto moved to ***_skipped queue when it responing

UCITY.API request to UCITY.USER's "ucity_us_mobilephonecomplete" queue and receive message but Masstransit auto move it to the skip queue
but other queues are working successfully
Masstransit Trace Log:
2018-03-23 15:59:22.5727|DEBUG|MassTransit.Messages|SEND rabbitmq://192.168.1.142/us/ucity_us_mobilephonecomplete 0b350000-dafb-1866-5b48-08d59093fd36 MQNamespace.USER.IMobilephoneCompleteRequest
2018-03-23 15:59:22.6077|DEBUG|MassTransit.Messages|SKIP rabbitmq://192.168.1.142/us/bus-WIN-VH8418VRIC5-UCITY.API-bc4oyyg49ccgcqg6bdk3br7trf?durable=false&autodelete=true N/A
Message Detail
he server reported 1 messages remaining.
Exchange bus-WIN-VH8418VRIC5-UCITY.API-bc4oyyg49ccgcqg6bdk3br7trf_skipped
Routing Key
Redelivered ●
Properties
message_id: 0b350000-dafb-1866-d0af-08d59093bae2
delivery_mode: 2
headers:
Content-Type: application/vnd.masstransit+json
publishId: 1
MT-Reason: dead-letter
MT-Host-MachineName: WIN-VH8418VRIC5
MT-Host-ProcessName: UCITY.API
MT-Host-ProcessId: 14648
MT-Host-Assembly: UCITY.API
MT-Host-AssemblyVersion: 1.0.0.0
MT-Host-MassTransitVersion: 4.0.1.1390
MT-Host-FrameworkVersion: 4.0.30319.42000
MT-Host-OperatingSystemVersion: Microsoft Windows NT 6.2.9200.0
content_type: application/vnd.masstransit+json
Payload
1060 bytes
Encoding: string
{
"messageId": "0b350000-dafb-1866-d0af-08d59093bae2",
"requestId": "0b350000-dafb-1866-8267-08d59093bade",
"conversationId": "0b350000-dafb-1866-0ddf-08d59093bae2",
"sourceAddress": "rabbitmq://192.168.1.142/us/ucity_us_mobilephonecomplete",
"destinationAddress": "rabbitmq://192.168.1.142/us/bus-WIN-VH8418VRIC5-UCITY.API-bc4oyyg49ccgcqg6bdk3br7trf?durable=false&autodelete=true",
"messageType": [
"urn:message:MQNamespace:IBaseResponseModel"
],
"message": {
"code": 4212,
"correlationId": "73989f86-51fd-4ae1-a467-a556a165f125",
"message": "COMPLETEMOBILEPHONE FAILD!USERWECHAT MOBILEPHONE COMPLETED",
"timeStamp": "2018-03-23T15:57:31.3218406+08:00"
},
"headers": {},
"host": {
"machineName": "WIN-VH8418VRIC5",
"processName": "UCITY.USER",
"processId": 8952,
"assembly": "UCITY.USER",
"assemblyVersion": "1.0.0.0",
"frameworkVersion": "4.0.30319.42000",
"massTransitVersion": "4.0.1.1390",
"operatingSystemVersion": "Microsoft Windows NT 6.2.9200.0"
}
}
the skipped queue what does it do
thanks for answer
Change this method to use the proper response type, instead of the base interface.
public async Task Consume(ConsumeContext<IMobilephoneCompleteRequest> context)
{
var dispatch = new UserDispatch(context.Message.CorrelationId);
try
{
var response = await dispatch.MobilephoneComplete(context.Message);
return context.RespondAsync(response);
}
catch (UCException ucEx)
{
var response = new AuthenticationResponse(context.Message.CorrelationId)
{
Code = ucEx.Code,
Message = ucEx.Message
};
return context.RespondAsync(response);
}
catch (Exception ex)
{
var response = new AuthenticationResponse(context.Message.CorrelationId)
{
Code = (int)EnumErrorStatus.UserError,
Message = ex.Message
};
Console.WriteLine(JsonConvert.SerializeObject(ex));
return context.RespondAsync(response);
}
}
The same problem for me also. The day after I figured out the problem, the cause seems to be using the Autofac register method for the current assembly like:
builder.RegisterConsumers(Assembly.GetExecutingAssembly());
Using the following (explicit registration for each consumer) instead was solved the binding problem:
builder.RegisterType<YouConsumerHandler>();
UCIT.API code
//execute request
public async Task<ResultOpenUserLogin> MobilephoneComplete(AuthenticationIdentity authenticationIdentity, ArguUserMobilephoneComplete arguUserMobilephoneComplete)
{
var response = await UserMobilephoneCompleteRequest.Client.Request(new UserMobilephoneCompleteRequest(CorrelationId)
{
DeviceType = authenticationIdentity.DeviceType,
IdUser = authenticationIdentity.IdUser,
Mobilephone = arguUserMobilephoneComplete.Mobilephone,
Passcode = arguUserMobilephoneComplete.Passcode
});// the request timeout
if (response.Code != 200)
{
throw new UCException(response.Code, response.Message);
}
return new ResultOpenUserLogin()
{
Token = response.Token,
UserInfoCompleted = !string.IsNullOrWhiteSpace(response.Mobilephone)
};
}
//UserMobilephoneCompleteRequest class detail
[RabbitMqUri(VirtualHost = "ucity_us", Queue = "ucity_us_mobilephonecomplete")]
public class UserMobilephoneCompleteRequest : BaseRequest, IMobilephoneCompleteRequest
{
public UserMobilephoneCompleteRequest(Guid? correlationId) : base(correlationId)
{
}
public int DeviceType { get; set; }
public int IdUser { get; set; }
public string Mobilephone { get; set; }
public string Passcode { get; set; }
public static IRequestClient<IMobilephoneCompleteRequest, IAuthenticationResponse> Client
{
get
{
return _requestClient;
}
}
private static IRequestClient<IMobilephoneCompleteRequest, IAuthenticationResponse> _requestClient
{
get; set;
}
public override void CreateClient(IBusControl busControl, Uri address)
{
_requestClient = busControl.CreateRequestClient<IMobilephoneCompleteRequest, IAuthenticationResponse>(address, TimeSpan.FromSeconds(10));
}
}
UCIT.USER code
//these codes work well
[RabbitMqUri(VirtualHost = "ucity_us", Queue = "ucity_us_mobilephonecomplete")]
public class MobilephoneCompleteConsumer : BaseConsumer<MobilephoneCompleteConsumer>, IConsumer<IMobilephoneCompleteRequest>
{
public async Task Consume(ConsumeContext<IMobilephoneCompleteRequest> context)
{
var dispatch = new UserDispatch(context.Message.CorrelationId);
IBaseResponseModel response = null;
try
{
response = await dispatch.MobilephoneComplete(context.Message);
}
catch (UCException ucEx)
{
response = new AuthenticationResponse(context.Message.CorrelationId)
{
Code = ucEx.Code,
Message = ucEx.Message
};
}
catch (Exception ex)
{
response = new AuthenticationResponse(context.Message.CorrelationId)
{
Code = (int)EnumErrorStatus.UserError,
Message = ex.Message
};
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(ex));
}
await context.RespondAsync(response);
}
}

MVC 4 application does not go to default page after login

I have created an MVC 4 application which targets .NET 4.0. After deploying to my production server, it will show the login page but will not redirect to the default page. However, when I add debugging, I can see that the authentication process works but then the error I am getting is an error that says it can't find my View for my Error Page and then shows my Error Page. It just seems that it will not go to my "Home/Index" page - even when I remove the authorize attribute. Of course the application works in development. Additionally, it will not go to my register page or forgot login page.
My Login Controller looks like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiforgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if(ModelStat.IsValid && _userService.Login(model.UserId, model.Password))
{
var user = _userService.GetUser(model.UserId);
var loggedInUser = new LoggedInUser
{
// Build the user for custom IPrincipal
};
var userData = JsonConvert.SerializeObject(loggedInUser);
var compressData = StringCompression.Compress(userData);
var authTicket = new FormsAuthenticationTicket(
1,
user.UserId,
DateTime.Now,
DateTime.Now.AddHours(1),
false,
compressData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
if(encTicket != null)
{
var faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = true
};
Response.Cookies.Add(faCookie);
}
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
_userService.UpdateUser(user);
_uow.Commit();
return Url.IsLocalUrl(returnUrl) ? (ActionResult)Redirect(returnUrl) : RedirectToAction("Index", "Home");
}
return View(model);
and in my Global.asax:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if(authCookie != null)
{
var decompressedData = StringCompression.Decompress(authTicket.UserData);
var loggedInUser = JsonConvert.DesrializeObject<LoggedInUser>(decompressedData);
var currrentUser = new CustomPrincipal(authTicket.Name)
{
// Build the CustomPrincipal from the loggedInUser
};
if(HttpContext.Current.User.Identity.IsAuthenticated)
{
HttpContext.Current.User = currentUser;
}
}
}
I hope that this is enough to give someone an idea of what I may be doing wrong. Somehow I feel that it is something small that I am missing. Thanks in advance.
~InDireStraits
Update:
After more troubleshooting, it would seem that the issue may have something to do with the fact that I am using a BaseController for specifying permissions but I am still baffled as to why the application works as intended in my development environment but not in production. To verify my IIS settings I installed the default MVC4 App to production which does not have .NET 4.5, and it runs. I am using VS 2012 so I do have 4.5. Could I somehow be introducing .NET 4.5 classes or functionality even if this targets .NET 4.0? At any rate, here is my BaseController code:
public class BaseController: Controller
{
private string _actionKey;
private const string PermisisionList = "permissionList";
private Dictionary<string, string> _requiredActionPermissions;
private static readonly IControllerActionService<ControllerAction> _actionService;
protected new CustomPrincipal User
{
get
{
return HttpContext.User as CustomPrincipal;
}
}
public BaseController(IControllerActionService<ControllerAction> actionService)
{
_actionService = actionService;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check to see if the PermissionList is loaded and load if necessary
if(!CacheLayer.Exists(PermissionList))
{
_requiredActionPermissions = _actionService.GetControllerActionDictionary();
CacheLayer.Add(_requiredActionPermissions, PermissionList);
}
else
{
_requiredActionPermission = CacheLayer.Get<Dictionary<string, string>>(PermissionList);
}
// Get the Controller/Action of the current request
_actionKey = string.Format("{0}-{1}", filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor.ActionName);
// If the user is authenticated, grab the permissions
if(filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var userPermissions = User.Permissions;
if(!_requiredActionPermissions.Values.Any(a=>a.Equals(_actionKey, StringComparison.OrdinalIgnoreCase)))
{
return;
}
if(userPermissions.Contains(_requiredActionsPermissions.FirstOrDefault(x=>x.Value == _actionKey).Key))
{
return;
}
filterContext.Result = new RedirectResult("~/Error/ErrorUnauthorized");
return;
}
if(!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if(!_requiredActionPermissions.Values.Any(a=>a.Equals(_actionKey, StringComparison.OrdinalIgnoreCase)))
{
return;
}
}
if(filterContext.HttpContext.Request.Url == null)
{
return;
}
if(filterContext.HttpContext.Request.Url.AbsolutePath == FormsAuthentication.LoginUrl)
{
return;
}
var redirectUrl = string.Format("?returnUrl={0}", filterContext.HttpContext.Request.Url.PathAndQuery);
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + redirectUrl, true);
}
UPDATE 2: Installed .NET 4.52 on Staging Server and the application now works as intended. The problem is that I will not be able to install this on the production server. I don't understand what it is that 4.5 is fixing that 4.0 does not seem to facilitate. HELLLLLLPPP!!!!!!
The answer can be found here. To summarize, I added an extra parameter to my route config that worked in 4.5 but not in 4.0. Will follow up on the linked question. Thanks