Reputation: 1208
I am seeing some very odd behavior when trying to do a redirect with .NET MVC. I read somewhere to not do the redirect in a subroutine so I changed that part of the code, but I am still having the problem. This only happens in production (particularly with bots), and I can't reproduce it in the dev environment. I added in some logging to try to help but still don't understand what is happening (it was a poor man's solution because I don't have access to put debug symbols on the production server). The call stack doesn't even make sense because it says there is a recursive call that doesn't exist. Thanks in advance for any help!
Error details:
Step Number: 16, LangID: 1033, Language: en, Culture: us -
System.NullReferenceException: Object reference not set to an instance of an object. at [REMOVED]_Web.Controllers.MVC.HomeController.setLanguage(String language, String _culture)User Name: Anonymous
URL: [REMOVED]'A=0
User Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-PT; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 (.NET CLR 3.5.30729)
Exception Details:System.Exception: Step Number: 16, LangID: 1033, Language: en, Culture: us - System.NullReferenceException: Object reference not set to an instance of an object.
at [REMOVED]_Web.Controllers.MVC.HomeController.setLanguage(String language, String _culture)
at [REMOVED]_Web.Controllers.MVC.HomeController.setLanguage(String language, String _culture)
at [REMOVED]_Web.Controllers.MVC.HomeController.Init(String language, String culture)
at [REMOVED]_Web.Controllers.MVC.HomeController.ProdCatSearch(String language, String culture)
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.b__3d()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.b__3f()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass33.b__32(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<>c__DisplayClass2b.b__1c()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.b__1e(IAsyncResult asyncResult)
at System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)
at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult)
at System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Source code:
Private Sub setLanguage(language As String, _culture As String)
Dim stepNumber As Integer = 0
Try
Dim langCookie As HttpCookie
Dim hasException As Boolean = False
Try
If (String.IsNullOrEmpty(language) OrElse String.IsNullOrEmpty(_culture)) Then
language = "en"
_culture = "us"
End If
stepNumber = 1
Dim languages = clsAppSettings.Instance.GetAppSettingTable(Of String)("Language")
For Each lang In languages
supportedLangs.Add(New LanguageInfo(lang))
Next
supportedLangs.Sort(Function(x, y) x.SortOrder.CompareTo(y.SortOrder))
langCookie = HttpContext.Request.Cookies.Get("langID")
stepNumber = 2
If langCookie IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(langCookie.Value) Then
stepNumber = 3
LangId = langCookie.Value
CultureInfo.CreateSpecificCulture(language + "-" + _culture)
stepNumber = 4
Else
stepNumber = 5
Dim culture As CultureInfo
If language.ToLower = "en" And _culture.ToLower = "us" Then
stepNumber = 6
Dim langIdFromBrowser As String = String.Empty
Dim browserLangs = Request.UserLanguages
For Each browserLang In browserLangs
culture = CultureInfo.CreateSpecificCulture(browserLang.ToLowerInvariant().Trim())
langIdFromBrowser = culture.LCID.ToString
If Not String.IsNullOrWhiteSpace(langIdFromBrowser) Then
If (From items In supportedLangs Where items.Id = CInt(langIdFromBrowser)).Any Then
LangId = langIdFromBrowser
Exit For
End If
End If
Next
stepNumber = 7
If String.IsNullOrEmpty(LangId) Then
LangId = "1033"
End If
Else
stepNumber = 8
culture = CultureInfo.CreateSpecificCulture(language + "-" + _culture)
LangId = culture.LCID.ToString
If Not String.IsNullOrWhiteSpace(LangId) Then
If Not (From items In supportedLangs Where items.Id = CInt(LangId)).Any Then
LangId = "1033"
End If
End If
stepNumber = 9
End If
' Correct for the fact that SharePoint uses Spanish Traditional vs Spanish Modern Sort
If LangId = "1034" Then
LangId = "3082"
End If
stepNumber = 10
End If
Catch ex As CultureNotFoundException
stepNumber = 11
If String.IsNullOrEmpty(LangId) Then
LangId = "1033"
End If
hasException = True
Finally
' Final check to ensure NO empty value ever makes it past this point.
If String.IsNullOrWhiteSpace(LangId) Then
LangId = "1033"
End If
langCookie = New HttpCookie("langID", LangId)
langCookie.Expires = Date.Now().AddYears(1)
Web.HttpContext.Current.Response.Cookies.Add(langCookie)
stepNumber = 12
If hasException OrElse CultureInfo.CreateSpecificCulture(language + "-" + _culture).LCID.ToString() <> LangId Then
stepNumber = 13
Dim newCulture As New CultureInfo(CInt(LangId))
stepNumber = 14
Dim cultureValues As String() = newCulture.Name.Split("-"c)
Dim rUrl As String = Request.Url().OriginalString()
Dim replacement As String = (cultureValues(0) + "-" + cultureValues(1)).ToLower()
rUrl = rUrl.Replace((language + "-" + _culture), replacement)
'Response.Redirect(rUrl, False)
'HttpContext.ApplicationInstance.CompleteRequest()
End If
stepNumber = 15
ViewData("SelectedLang") = LangId
ViewData("SupportedLangs") = supportedLangs
transObj = Translations.GetTranslations(LangId)
ViewData("Translations") = New JavaScriptSerializer().Serialize(transObj)
stepNumber = 16
End Try
Catch ex As Exception
Throw New Exception(String.Format("Step Number: {0}, LangID: {1}, Language: {2}, Culture: {3} - {4}", stepNumber, LangId, language, _culture, ex.ToString()))
End Try
End Sub
Upvotes: 1
Views: 281
Reputation: 1208
To resolve this post so no one potentially wastes time on reading that huge method and to potentially help someone, I'm going to write up the solution.
First of all, @b.pell started off on the right foot by suggesting to fix the logging. I changed stepNumber
to be a list of ints then just did String.Join(" ", steps)
to print it to the log.
Secondly, this was a poor use of Finally
. Finally
should really just be used for cleanup. In this case, the code in the Finally
block shouldn't be running when an exception is thrown.
Another obstacle is this method is huge, making things difficult to isolate, especially when you are running with no debug symbols and just have a stack trace.
Ultimately, this line of code was creating the null causing the NullReferenceException
. So the fix is just a simple null check.
culture = CultureInfo.CreateSpecificCulture(language + "-" + _culture)
This could have been tracked down very quickly if we were able to drop the debug symbols on the production server or done some of the other things I mentioned.
Upvotes: 2