Generate a URL inside Application_Start() - asp.net-mvc

ASP.NET MVC 5
T4MVC (https://github.com/T4MVC/T4MVC)
ABP (http://aspnetboilerplate.com/)
I know it is possible to use T4MVC to generate a URL based on a specific action using UrlHelper(). See SO and here. However I ultimately need to do this during Application_Start() due to ABP requirements.
So in essence within Application_Start() I have:
//Areas
AreaRegistration.RegisterAllAreas();
//Routes
RouteConfig.RegisterRoutes(RouteTable.Routes);
//Get the URL
var urlHelper = new UrlHelper();
var url = urlHelper.ActionAbsolute(MVC.Home.Index())
however this blows up with the following exception:
'UrlHelper.ActionAbsolute(MVC.Home.Index())' threw an exception of type 'System.NullReferenceException'
Data: {System.Collections.ListDictionaryInternal}
HResult: -2147467261
HelpLink: null
InnerException: null
Message: "Object reference not set to an instance of an object."
Source: "T4MVCExtensions"
StackTrace: " at System.Web.Mvc.T4Extensions.ActionAbsolute(UrlHelper urlHelper, ActionResult result)"
TargetSite: {System.String ActionAbsolute(System.Web.Mvc.UrlHelper, System.Web.Mvc.ActionResult)}
however if I inspect the contents of things (at runtime) they appear to all be populated:
MVC.Home.Index()
{T4MVC_System_Web_Mvc_ActionResult}
Action: "Index"
Controller: "Home"
Protocol: null
RouteValueDictionary: {System.Web.Routing.RouteValueDictionary}
MVC.Home.Index().GetRouteValueDictionary()
{System.Web.Routing.RouteValueDictionary}
Count: 3
Keys: Count = 3
Values: Count = 3
Results View: Expanding the Results View will enumerate the IEnumerable
So can anyone shed any light on why this is blowing up? Or put another way how do I get the absolute url for an action using T4MVC inside Application_Start()?

The common theme between the links you provided were that those calls were made in an action which means that a request has already been materialized. Meaning that a context already exists. At startup there are no requests as yet. That is why you are getting the error.

Related

MVC Custom error and exception handling - standard through out the project

I am new to ASP.NET MVC , Kendo UI (razor) , Jquery. I have an application that throws 3 kinds of errors
Unhanded exceptions (400, 403,500 503 etc) - throw generic exceptions
Expected exceptions/ errors (custom exceptions) - e.g. Trying to create a contact that already exist in the system - The system needs to throw "Contact_duplicated_exception" and show this to the user as "Contact was previously created. This action could not proceed".
Model state errors (UI). Errors that I add to modelstate to showup on the page using #Html.ValidationSummary(true)
What is the best standard way of handling the above throughout the application ?
I need to send these messages back to the user using Jquery Ajax [POST].
I have used the following concepts but I need to implement a standard way of dealing with the above
1. I have used ELMAH (for unhanded exceptions)
2. Application_Error(object sender, EventArgs e) in global.asax.cs
3. Custom HandleErrorArrtibute
public class HandleErrorWithAjaxFilter : HandleErrorAttribute, IExceptionFilter
Thanks!
For #1 and #3, you're looking good, IMHO. However, I think your weak-point is #2 and here is why:
If the exception is expected it should not be allowed to fall to Application_Error; because, well... it's expected, it's workflow, not an exception.
Therefore as a reaction to user input and part of workflow, it should be handled as part of #3.
So, in your shoes, I would go about in the specific instance of finding duplicates adding a validation attribute onto the potentially duplicate class like so:
[AttributeUsage(AttributeTargets.Class)]
public class UniqueContactAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
bool isValid = true;
Contact contact = value as Contact;
if(contact != null)
{
// check for your duplicate in the database and set isValid to false if you find one.
}
return isValid;
}
}
Usage for your metadata class:
[UniqueContact(ErrorMessage = "Contact was previously created. This action could not proceed.")]
public class Contact_Validation
{
}

Overload Index() Method in MVC Controller

I want to access both /Blog and /Blog/1 where "1" is the ID of the Blog. Here is my code:
'
' GET: /Blog/
Function Index() As ViewResult
Return (View(db.Blogs.ToList()))
End Function
'
' GET: /Blog/(Integer)
Function Index(id As Integer) As ViewResult
Dim blog As Blog = db.Blogs.Find(id)
Return View("Details", "_MyLayout", blog)
End Function
It gives the error:
Server Error in '/' Application.
The current request for action 'Index' on controller type
'BlogController' is ambiguous between the following action methods:
System.Web.Mvc.ViewResult Index() on type
GemcoBlog.GemcoBlog.BlogController System.Web.Mvc.ViewResult
Index(Int32) on type GemcoBlog.GemcoBlog.BlogController
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.Reflection.AmbiguousMatchException: The
current request for action 'Index' on controller type 'BlogController'
is ambiguous between the following action methods:
System.Web.Mvc.ViewResult Index() on type
GemcoBlog.GemcoBlog.BlogController System.Web.Mvc.ViewResult
Index(Int32) on type GemcoBlog.GemcoBlog.BlogController
Source Error:
An unhandled exception was generated during the execution of the
current web request. Information regarding the origin and location of
the exception can be identified using the exception stack trace below.
How can I overload the Index() method?
Edit:
I am also trying to combine them like so:
'
' GET: /Blog/
Function Index(id As Integer) As ViewResult
If (id) Then
Dim blog As Blog = db.Blogs.Find(id)
'Return View(blog)
Return View("Details", "_MyLayout", blog)
Else
Return (View(db.Blogs.ToList()))
End If
'Return View(db.Blogs.Where(Function(x) x.Name = "Test").ToList())
End Function
However, the error I get is:
Server Error in '/' Application.
The parameters dictionary contains a null entry for parameter 'id' of
non-nullable type 'System.Int32' for method 'System.Web.Mvc.ViewResult
Index(Int32)' in 'Blog.Blog.BlogController'. An optional
parameter must be a reference type, a nullable type, or be declared as
an optional parameter. Parameter name: parameters
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentException: The parameters dictionary
contains a null entry for parameter 'id' of non-nullable type
'System.Int32' for method 'System.Web.Mvc.ViewResult Index(Int32)' in
'Blog.Blog.BlogController'. An optional parameter must be a
reference type, a nullable type, or be declared as an optional
parameter. Parameter name: parameters
Source Error:
An unhandled exception was generated during the execution of the
current web request. Information regarding the origin and location of
the exception can be identified using the exception stack trace below.
You cannot have 2 actions on the same controller accessible with the same HTTP verb. So either change the action name or you will have to disambiguate using different HTTP verbs. For example:
<HttpPost>
Function Index(id As Integer) As ViewResult
Dim blog As Blog = db.Blogs.Find(id)
Return View("Details", "_MyLayout", blog)
End Function
But since the other action also seems to be fetching data I guess that you don't want to make it POST accessible only. So simply rename it in this case. Sticking to standard RESTful conventions you could use Index for returning a list of resources and Show to return a particular resource:
Function Index() As ViewResult
Return (View(db.Blogs.ToList()))
End Function
'
' GET: /Blog/(Integer)
Function Show(id As Integer) As ViewResult
Dim blog As Blog = db.Blogs.Find(id)
Return View("Details", "_MyLayout", blog)
End Function
There are a couple of ways you could do this. The easiest would be to rename the first method to "ShowBlog" or whatever you want, then setup a route in your global.asax that routes to the /Blog route without a parameter.
For example (in c#):
routes.MapRoute("Blog", "Blog", new { controller = "Blog", action = "ShowBlog" });
Make sure the MapRoute comes before the default route.
To make your second method work, you would need to make the id nullable, and then check for null in your method.
Since it is not nullable, it automatically assumes you are providing an id by default. Make the Id a Nullable Integer, and it will work for both URLs.
Function Index(id As Nullabe( Of Integer )) As ViewResult
Small amendment to Erik's post to make it work (I am using MVC4)
routes.MapRoute("Blog", "Blog/{id}", new { controller = "Blog", action = "ShowBlog" });

Error Handling in asp.net mvc 3

Is there a built in or a proper way to handle errors in asp.net mvc 3?
This is what I want to do:
If the application crashes, or throws an error, it goes to a specific error page.
I can throw my own error from the controller action. (and it goes to an error page).
I found the following ways:
I see there is a long way to do it
here. (for v1 and v2 but also
applies to v3).
Using errorhandle attribute here.
How do I handle this the proper way?
If the solution is similar or is like #1 in the list above, I am using ninject and I have not created a base class. How do I still do this?
For Global Error Handling
All you have to do is change the customErrors mode="On" in web.config page
Error will be displayed through Error.cshtml resides in shared folder.
Make sure that Error.cshtml Layout is not null.
[It sould be something like: #{ Layout = "~/Views/Shared/_Layout.cshtml"; }
Or remove Layout=null code block]
A sample markup for Error.cshtml:-
#{ Layout = "~/Views/Shared/_Layout.cshtml"; }
#model System.Web.Mvc.HandleErrorInfo
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<h2>
Sorry, an error occurred while processing your request.
</h2>
<p>Controller Name: #Model.ControllerName</p>
<p>Action Name : #Model.ActionName</p>
<p>Message: #Model.Exception.Message</p>
</body>
</html>
For Specific Error Handling
Add HandleError attribute to specific action in controller class. Provide 'View' and 'ExceptionType' for that specific error.
A sample NotImplemented Exception Handler:
public class MyController: Controller
{
[HandleError(View = "NotImplErrorView", ExceptionType=typeof(NotImplementedException))]
public ActionResult Index()
{
throw new NotImplementedException("This method is not implemented.");
return View();
}
}
I would suggest implementing a custom HandleErrorAttribute action filter.
See this link for more details:
http://msdn.microsoft.com/en-us/library/dd410203%28v=vs.90%29.aspx
Setting up a HandleErrorAttribute action filter gives you complete control over which actions are handled by the filter, and it's easy to set at the controller level, or even at the site level by setting it up on a custom base controller, and having all of your controllers inherit from the base controller.
Something else I do with this, is I have a separate HandleJsonErrorAttribute that responds to Ajax calls by returning a Json response, rather than the custom page.
UPDATE:
Per some questions below, here is an example of a HandleJsonErrorAttribute that I use:
public class HandleJsonErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
var serviceException = filterContext.Exception as ServiceException;
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult { Data = new { message = serviceException == null ? "There was a problem with that request." : serviceException.Message } };
filterContext.ExceptionHandled = true;
}
}
And here is the jQuery that I use to handle these unhanded exceptions:
$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
showPopdown($.parseJSON(jqXHR.responseText).message);
});
This allows my Ajax methods to be very lightweight -- they just handle returning normal Json, and in the event of an unhanded exception, a message w/ an error status code gets wrapped in Json and returned.
Also, in my implementation, I have a custom ServiceException that I throw from services, and this sends the message from the service layer instead of a generic message.
The easiest way I think you can do that is using the elmah library.
Take a look at this: http://code.google.com/p/elmah/wiki/MVC and this
http://www.hanselman.com/blog/ELMAHErrorLoggingModulesAndHandlersForASPNETAndMVCToo.aspx
I think the easiest way is using ExceptionHandler attribute since it's ready to use anytime you create a new ASP.NET MVC 3 project. You can still configure Web.config to use a custom error page and handling exceptions in global Application_Error method as usual but when an exception occurs the URL is not displayed as nice as the new MVC 3's way.
you can create custom exception in MVC if you want to customize a way of exception handling.
you can find useful post here .
http://www.professionals-helpdesk.com/2012/07/creating-custom-exception-filter-in-mvc.html

How to get rid of that HTML error report in ASP.NET MVC?

All I need would be just the error message in plain text. But ASP.NET is doing some HTML report output from every error.
I have a jquery ajax call and when an error is thrown I'm getting all that crap over to the client side.
I've created a filter attribute but didn't helped.
public class ClientErrorHandler : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
var responce = filterContext.RequestContext.HttpContext.Response;
responce.Write(filterContext.Exception.Message);
responce.ContentType = MediaTypeNames.Text.Plain;
filterContext.ExceptionHandled = true;
}
}
EDIT
I'm seeing this
and I'd like to see just what is in here filterContext.Exception.Message
It looks to me like the reason why you cannot correctly handle the exception is because it happens outside of the MVC pipeline. If you look at the stack trace in the code you posted there is no reference to System.Web.Mvc code (the firing of exception filters when an exception occurs is called from ControllerActionInvoker.InvokeAction).
The stack trace indicates that the exception happens late in the ASP.NET pipeline (OnEndRequest) and that it's coming through the Autofac component.
To capture this error you would have to subscribe to the HttpApplication's Error event. See the following article on creating a global error handler: http://msdn.microsoft.com/en-us/library/994a1482.aspx . In this event you can handle the error and redirect to a custom error page.
you need to return a ContentResult
ContentResult result = new ContentResult();
result.Content = filterContext.Exception.Message;
result.ContentType = MediaTypeNames.Text.Plain;
filterContext.Result = result;
filterContext.ExceptionHandled = true;
Since you're using JQuery and WCF (by the details of your error), you might want to take a look at this article on how to handle service faults elegantly between jQuery and WCF - you might have to rework your service if you are able to do so.

ASP.NET MVC QueryString defaults overriding supplied values?

Using ASP.NET MVC Preview 5 (though this has also been tried with the Beta), it appears that querystring defaults in a route override the value that is passed in on the query string. A repro is to write a controller like this:
public class TestController : Controller
{
public ActionResult Foo(int x)
{
Trace.WriteLine(x);
Trace.WriteLine(this.HttpContext.Request.QueryString["x"]);
return new EmptyResult();
}
}
With route mapped as follows:
routes.MapRoute(
"test",
"Test/Foo",
new { controller = "Test", action = "Foo", x = 1 });
And then invoke it with this relative URI:
/Test/Foo?x=5
The trace output I see is:
1
5
So in other words the default value that was set up for the route is always passed into the method, irrespective of whether it was actually supplied on the querystring. Note that if the default for the querystring is removed, i.e. the route is mapped as follows:
routes.MapRoute(
"test",
"Test/Foo",
new { controller = "Test", action = "Foo" });
Then the controller behaves as expected and the value is passed in as the parameter value, giving the trace output:
5
5
This looks to me like a bug, but I would find it very surprising that a bug like this could still be in the beta release of the ASP.NET MVC framework, as querystrings with defaults aren't exactly an esoteric or edge-case feature, so it's almost certainly my fault. Any ideas what I'm doing wrong?
The best way to look at ASP.NET MVC with QueryStrings is to think of them as values that the route does not know about. As you found out, the QueryString is not part of the RouteData, therefore, you should keep what you are passing as a query string separate from the route values.
A way to work around them is to create default values yourself in the action if the values passed from the QueryString are null.
In your example, the route knows about x, therefore your url should really look like this:
/Test/Foo or /Test/Foo/5
and the route should look like this:
routes.MapRoute("test", "Test/Foo/{x}", new {controller = "Test", action = "Foo", x = 1});
To get the behavior you were looking for.
If you want to pass a QueryString value, say like a page number then you would do this:
/Test/Foo/5?page=1
And your action should change like this:
public ActionResult Foo(int x, int? page)
{
Trace.WriteLine(x);
Trace.WriteLine(page.HasValue ? page.Value : 1);
return new EmptyResult();
}
Now the test:
Url: /Test/Foo
Trace:
1
1
Url: /Test/Foo/5
Trace:
5
1
Url: /Test/Foo/5?page=2
Trace:
5
2
Url: /Test/Foo?page=2
Trace:
1
2
Hope this helps clarify some things.
One of my colleagues found a link which indicates that this is by design and it appears the author of that article raised an issue with the MVC team saying this was a change from earlier releases. The response from them was below (for "page" you can read "x" to have it relate to the question above):
This is by design. Routing does not
concern itself with query string
values; it concerns itself only with
values from RouteData. You should
instead remove the entry for "page"
from the Defaults dictionary, and in
either the action method itself or in
a filter set the default value for
"page" if it has not already been set.
We hope to in the future have an
easier way to mark a parameter as
explicitly coming from RouteData, the
query string, or a form. Until that is
implemented the above solution should
work. Please let us know if it
doesn't!
So it appears that this behaviour is 'correct', however it is so orthogonal to the principle of least astonishment that I still can't quite believe it.
Edit #1: Note that the post details a method of how to provide default values, however this no longer works as the ActionMethod property he uses to access the MethodInfo has been removed in the latest version of ASP.NET MVC. I'm currently working on an alternative and will post it when done.
Edit #2: I've updated the idea in the linked post to work with the Preview 5 release of ASP.NET MVC and I believe it should also work with the Beta release though I can't guarantee it as we haven't moved to that release yet. It's so simple that I've just posted it inline here.
First there's the default attribute (we can't use the existing .NET DefaultValueAttribute as it needs to inherit from CustomModelBinderAttribute):
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class DefaultAttribute : CustomModelBinderAttribute
{
private readonly object value;
public DefaultAttribute(object value)
{
this.value = value;
}
public DefaultAttribute(string value, Type conversionType)
{
this.value = Convert.ChangeType(value, conversionType);
}
public override IModelBinder GetBinder()
{
return new DefaultValueModelBinder(this.value);
}
}
The the custom binder:
public sealed class DefaultValueModelBinder : IModelBinder
{
private readonly object value;
public DefaultValueModelBinder(object value)
{
this.value = value;
}
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
var request = bindingContext.HttpContext.Request;
var queryValue = request .QueryString[bindingContext.ModelName];
return string.IsNullOrEmpty(queryValue)
? new ModelBinderResult(this.value)
: new DefaultModelBinder().BindModel(bindingContext);
}
}
And then you can simply apply it to the method parameters that come in on the querystring, e.g.
public ActionResult Foo([Default(1)] int x)
{
// implementation
}
Works like a charm!
I think the reason querystring parameters do not override the defaults is to stop people hacking the url.
Someone could use a url whose querystring included controller, action or other defaults you didn't want them to change.
I've dealt with this problem by doing what #Dale-Ragan suggested and dealing with it in the action method. Works for me.
I thought the point with Routing in MVC is to get rid of querystrings. Like this:
routes.MapRoute(
"test",
"Test/Foo/{x}",
new { controller = "Test", action = "Foo", x = 1 });

Resources