My code (asp.net 5, mvc 6, rc-1):
public class MyView: RazorView
{
// Constructor passing everything to base class
public async override Task RenderAsync(ViewContext context)
{
context.Writer.WriteLine("");
context.Writer.WriteLine("<!-- View: {0} -->", Path);
await base.RenderAsync(context);
context.Writer.WriteLine("");
context.Writer.WriteLine("<!-- View: {0} end -->", Path);
}
}
However this override changes somehow execution of overridden method, causing all errors in PartialViews to be executed after response has already started (so developer exception page cannot be executed = 502 bad gateway).
My goal is to decorate all views with comments indicating what file is it, just like angular js does.
Edit:
Code snippet above generally works (all views are decorated with comments) - problem occurs only when exception is thrown.
Related
Requirement
I have an ASP.Net MVC application that works with a number of different libraries. As with most libraries, various function calls may cause one of many different exceptions to be thrown.
Currently, whenever any exception is thrown, then MVC application 'handles' them and returns an "internal server error" (code 500) back to the client (eg. web browser).
This is fine for most cases, however, there is one specific exception type (in this case UnauthorizedAccessException) that I would like to result in an "Unauthorized" (code 401) status being sent in the response, instead of the usual 500 error.
Current Attempt
I did a fair bit of research and it looks like the bets way to 'catch' all exceptions and process them is by using the Application_Error method. So I tried the following implementation of Application_Error in the MvcApplication class:
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if(ex is UnauthorizedAccessException)
{
Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
}
}
Problem
The problem here is that although I can debug and see that Response.StatusCode is being set to 401, the client/browser is still receiving a 500 error.
I expect that there is a very good reason why this is happening, but I have exhausted my brain thinking of search terms that will get me the answer I need.
Question
In short, what do I need to do in order to get the behaviour I am looking for?
For additional information, ultimately what I want is for an UnauthorizedAccessException to have the same behaviour as how MVC handles unauthenticated requests (which redirects to login page). However, I also need it to work for AJAX requests in that my javascript can check for 401 error and do some specific logic (in which case a response redirect to login page is not workable)
A clever way to go about doing that is to create a Base Controller that your controllers inherit over the default Controller. There you inherit the default Controller class and override the OnException method.
public abstract class BaseController : Controller
{
protected override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
var responseCode = Response.StatusCode;
var exception = filterContext.Exception;
switch (exception.GetType().ToString())
{
case "UnauthorizedAccessException":
responseCode = 401;
filterContext.ExceptionHandled = true;
break;
}
Response.StatusCode = responseCode;
base.OnException(filterContext);
}
}
The trick that makes it work is filterContext.ExceptionHandled = true; if you don't set this to true, the server will return 500.
Your controllers will inherit the BaseController;
public class UserController : BaseController
{
public ActionResult Index(){
return View();
}
}
What you'll need to add to this code is your redirect to the login page, to your OnException method (if needed). I would do it for you but I don't have enough time to write and test it for you.. currently waiting for automated tests to finish.. :-)
Edit:
I did not realize your view could throw errors too, that obviously won't be handled by the controller.
In this case we can revert to the original Application_Error method on Global.asax.
What we need is two lines of code..
Response.StatusCode = 401;
Response.End();
First line sets the status code to 401,
Second line ends the execution at this point and triggers EndRequest event, so the StatusCode won't be modified to 500.
If you want to attach a message with your response:
Response.Write("Oops, you're not authorized...");
It would be a good idea to call Response.Clear(); before starting to modify the response object inside your Error Handler.
I need to run some c# code each time any page based on _layout.cshtml is viewed. I don't want to put something in every controller.cs file, just something central like you used to have in ASP.NET's MasterPage.cs
Can't get this
Run a method in each request in MVC, C#?
or this
#Html.Action in Asp.Net Core
to run, not sure if it's because they're not CORE 2.0.0, I just get a lot of compilation errors. All I want to do is be able to run some code like this
public class myClass {
public static bool returnTrue() {
return true;
}
}
every time each page is loaded.
You can accomplish this with an action filter
public class GlobalFilter : IActionFilter{
public void OnActionExecuting(ActionExecutingContext context) {
//code here runs before the action method executes
}
public void OnActionExecuted(ActionExecutedContext context) {
//code here runs after the action method executes
}
}
Then in the Startup.cs file in the ConfigureServices method you wire up the ActionFilter like so:
services.AddScoped<GlobalFilter>(); //add it to IoC container.
services.AddMvc().AddMvcOptions(options => {
options.Filters.AddService(typeof(GlobalFilter)); //Tell MVC about it
});
Then you can place code in this ActionFilter which can run before every action method and you can place code in it to run after every action method. See code comments.
Through the context parameter you have access to the Controller, Controller Name, Action Descriptor, Action Name, Request object (Including the path) and so on, so there is lots of info available for you to determine which page you want to execute the code for. I'm not aware of a specific property that will tell you if the page is using _layout.cshtml but you could probably deduce that based on the other properties I mentioned.
Enjoy.
Filter would also work, but the correct way to go in .Net Core is Middleware. You can read more about it here.
If it's something simple as your example, you can go with the first examples on the link like:
app.Use(async (context, next) =>
{
returnTrue();
await next.Invoke();
});
Let me know if it helped!
I recently ran into a problem where I was developing an API which talked to two data sources in some methods. The POST for a couple methods modified SQL data through the use of entity framework as well a data source using as an old SDK that was STA COM based. To get the STA COM SDK code to work correctly from within the API methods, I had to create method attributes that identified the methods as needing to be single threaded. I forced single threading by overriding the InvokeActionAsync() method from ApiControllerActionInvoker. If a method was not given an attribute to be single threaded, the overridden invoker simply used the normal base class InvokeActionAsync().
public class SmartHttpActionInvoker: ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext context, CancellationToken cancellationToken)
{
// Determine whether action has attribute UseStaThread
bool useStaThread = context.ActionDescriptor.GetCustomAttributes<UseStaThreadAttribute>().Any();
// If it doesn't, simply return the result of the base method
if (!useStaThread)
{
return base.InvokeActionAsync(context, cancellationToken);
}
// Otherwise, create an single thread and then call the base method
Task<HttpResponseMessage> responseTask = Task.Factory.StartNewSta(() => base.InvokeActionAsync(context, cancellationToken).Result);
return responseTask;
}
}
public static class TaskFactoryExtensions
{
private static readonly TaskScheduler _staScheduler = new StaTaskScheduler(numberOfThreads: 1);
public static Task<TResult> StartNewSta<TResult>(this TaskFactory factory, Func<TResult> action)
{
return factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, _staScheduler);
}
}
public static void Register(HttpConfiguration config)
{
....
config.Services.Replace(typeof(IHttpActionInvoker), new SmartHttpActionInvoker());
...
}
This worked well until I noticed something odd. My Logging database was logging duplicate records when a method NOT marked as single threaded was throwing a HttpResponseException back to the client. This behavior did not exist when the same method returned OK().
Debugging, I noticed the code execute in the API method, then reach the throw statement. The next line after the exception was thrown to be shown in debugger was the InvokeActionAsync() code I wrote. Following this the method was run again, in full, hitting the thrown exception, the action invoker, and then returning the result to the client. Effectively, it appears my use of overriding the InvokeActionAsync causes the Action invoker to be called twice somehow... but I am not sure how.
EDIT: Confirmed that the System.Threading.Thread.CurrentThread.ManagedThreadId for the current thread when it is thrown and logged is different for each execution of the API method. So, this reinforces my belief two threads are being created instead of one. Still not sure why.
Anyone have any experience with overriding the InvokeActionAsync behavior that might be able to explain this behavior? Thanks!
My site is facing unwanted robot call to a specific MVC controller with varying action name. It causes huge exception for hitting into nonsexist action. Our logging system almost flooded with error.
We have decided to adopt following approach as a work around.
Efficiently handle request for non-exist action so it doesn't throw exception
Throttle down robot call
We have written below code to achieve this. Can any one please review the approach.
protected override void HandleUnknownAction(string actionName)
{
if (this.ActionInvoker.InvokeAction(this.ControllerContext,
"ReturnErrorForUnknownAction")) return;
}
public ActionResult ReturnErrorForUnknownAction()
{
return Task.Delay(2000).ContinueWith(t =>
{
return new HttpStatusCodeResult(System.Net.HttpStatusCode.NotFound);
}).Result;
}
Above code is working fine but not sure if "ReturnErrorForUnknownAction" is a blocking call. As per my knowledge Task.Result blocks current thread.
My original intention is to implement Asynchronous delay before sending 404 status.
using Task.Result can cause locks. change the method to something like...
public async Task<ActionResult> ReturnErrorForUnknownAction()
{
return await Task.Delay(2000).ContinueWith(t =>
{
return new HttpStatusCodeResult(System.Net.HttpStatusCode.NotFound);
});
}
I have seen the model validation from here (Under section: Handling Validation Errors).
The code-snippet is as below in Web API
public class ValidateModel : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
base.OnActionExecuting(actionContext);
}
}
The problem is upon validation of model, if there were any errors, it assigns an model state invalid exception.
And after that, before going to the actual method (which is decorated with this [ValidateModel] attribute), WebAPI simply returns a 400 request.
But how ? Which function is returning a HTTP 400?
What happens after this method is done executing? Where does the control flow ?
EDIT:
The action that I am applying this attribute to is a normal one.
[ValidateModel]
public IHttpActionResult Post([FromBody]Request)
{
//do normal business logics here.
return Ok(SuccessMessage);
}
To understand the control flow you first need to visit this link here -
Action Attribute Class Reference
Check the method sections there. If clearly states that, OnActionExecuting is performed before that particular action, decorated with this attribute, is executed and OnActionExecuted is performed after the execution is complete. Since you are implementing OnActionExecuting like this -
public class ValidateModel : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
base.OnActionExecuting(actionContext);
}
}
and since the error is thrown inside this method, like this -
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
Your method will only execute if the OnActionExecuting method finds a valid model. And from the error 400, it seems that your current model state is not valid and thus it fails to succeed and your method will never be executed unless you provide a valid model. Set a debug point inside this method and you can find out why it fails.
By the way, the exception is not thrown its just a standard response that is handled by -
base.OnActionExecuting(actionContext);
http://msdn.microsoft.com/en-us/library/system.web.mvc.actionfilterattribute(v=vs.118).aspx
To find the error point just create a break point on Application_Error in Global.asax and follow the error details and stack trace to find the origin for the same.
When method OnActionExecuting is called it checks to see if the Model is valid. If Model is not valid
actionContext.Response will be calling server which will return 400 code back meaning the it was bad request.
It all depends how you are calling this web api. Normally you can have jquery or some other libraries or code behind from asp.net making the call to the webapi's with appropriate object. That object is first checked if it is valid or not. If it is valid it will keep processing nothing is returned back. If object state is invalid than it will return the status code 400. If it is returning 200 than it means your object is valid and web api executed and is returning 200. This 200 is not being returned from webapi but from EF after data was written sucessfully.
If you want to use try catch block than you dont need to check if model is valid. Just pass through the url with object and wait for response than catch it and display.
If your Request data type is defined as struct, or an abstract class, it cannot be instantiated, and that might be the probable cause. If is a struct, just make it Nullable, if it is an abstract class or interface you can either create your own ModelBinder to deal with the creation, or you can change it with a concrete implementation.
In VBA.NET API REST
Validate model
Imports System.ComponentModel.DataAnnotations
Public Class Client
<Required(ErrorMessage:="Dpi is required")>
Public Property Dpi As String
<MaxLength(16, ErrorMessage:="El NAME no debe ser mayor a 16 caracteres.")>
Public Property NAME As String
End Class
CONTROLLER
<Route("UPDATE/client")>
<ActionName("description to do...")>
<HttpPost>
Public Function saveClient(ByVal cli As Client)
If ModelState.IsValid Then
return Request.CreateResponse(HttpStatusCode.OK, True)
Else
Return BadRequest(ModelState)
End If
End Function