MVC5/API2 CreateErrorResponse in custom ActionFilterAttribute OnActionExecuting - asp.net-mvc-5

With MVC4 I was able to create and register a global action filter that would check the model state prior to the action's execution and return the serialized ModelState before any damage could be done.
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
However, with MVC5, I am having trouble finding Request and therefore CreateErrorResponse
public override void OnActionExecuting(ActionExecutingContext nActionExecutingContext)
{
if (!nActionExecutingContext.Controller.ViewData.ModelState.IsValid)
{
nActionExecutingContext.Result = // Where is Request.CreateErrorResponse ?
}
}
I realize that I could create a custom response class to assign to Result but I'd rather use what's built-in if CreateErrorResponse is still available.
Any idea where I can find it relative to an ActionExecutingContext in MVC5 / Web API 2?

I know this is an old question but I recently had the same problem and solved it using
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}

Related

SwaggerRequestExample attribute does not work in ASP.NET MVC 5 (.NET Framework 4.5.2)

I am toying with Swashbuckle.Examples package (3.10.0) in an ASP.NET MVC project. However, I cannot make request examples appear within the UI.
Configuration (SwaggerConfig.cs)
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c => {
c.SingleApiVersion("v1", "TestApp.Web");
c.IncludeXmlComments(string.Format(#"{0}\bin\TestApp.Web.xml", System.AppDomain.CurrentDomain.BaseDirectory));
c.OperationFilter<ExamplesOperationFilter>();
c.OperationFilter<DescriptionOperationFilter>();
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
})
.EnableSwaggerUi(c => { });
}
Request example classes
public class EchoRequestExample : IExamplesProvider
{
public object GetExamples()
{
return new EchoInput { Value = 7 } ;
}
}
public class EchoInput
{
public int Value { get; set; }
}
Action
[HttpGet]
[Route("Echo")]
[CustomApiAuthorize]
[SwaggerRequestExample(typeof(EchoInput), typeof(EchoRequestExample))]
[ResponseType(typeof(EchoServiceModel))]
public HttpResponseMessage Echo([FromUri] EchoInput model)
{
var ret = new EchoServiceModel
{
Username = RequestContext.Principal.Identity.Name,
Value = value
};
return Request.CreateResponse(HttpStatusCode.OK, ret);
}
Swagger UI shows xml comments and output metadata (model and an example containing default values), but shows no request example. I attached to process and EchoRequestExample.GetExamples is not hit.
Question: How to make SwaggerRequestExample attribute work in ASP.NET MVC 5?
Note: Windows identity is used for authorization.
I received an answer from library owner here:
Swagger request examples can only set on [HttpPost] actions
It is not clear if this is a design choice or just a limitation, as I find [HttpGet] examples also relevant.
I know the feeling, lot's of overhead just for an example, I struggle with this for a while, so I created my own fork of swashbuckle, and after unsuccessful attempts to merge my ideas I ended up detaching and renaming my project and pushed to nuget, here it is: Swagger-Net
An example like that will be:
[SwaggerExample("id", "123456")]
public IHttpActionResult GetById(int id)
{
Here the full code for that: Swagger_Test/Controllers/IHttpActionResultController.cs#L26
Wondering how that looks like on the Swagger-UI, here it is:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=IHttpActionResult#/IHttpActionResult/IHttpActionResult_GetById

OutputCache and injected ActionParameters

We use a ActionFilterAttribute to inject some parameters into actions and it works great.
But when we add OutputCache it varies exclusively on "MyID" when Html.RenderAction() is used and not when surfing directly to the action.
Any ideas how to get OutputCache to always recognize "MyID"?
Controller
[SiteIDs, OutputCache]
public ActionResult SiteContent(string myID)
{
return Content(myID);
}
ActionFilter
public class SiteIDs : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters.ContainsKey("MyID"))
{
filterContext.ActionParameters["MyID"] = GetMyIDByHostname();
}
base.OnActionExecuting(filterContext);
}
}
With the OutputCacheAttribute, action filters will not execute when a page is retrieved from the cache. You should probably use mvcdonutcaching in order to have the action filters executed even when retrieving from the cache. I'd recommend reading this.
Option 1
According to this answer, you just need to use VaryByParam = "*" and it will automatically vary by the parameters you pass the action method.
[SiteIDs, OutputCache(VaryByParam = "*")]
public ActionResult SiteContent(string myID)
{
return Content(myID);
}
However, that may not work by using an IActionFilter (haven't tried it). You might try using an IValueProvider instead (which is a cleaner way to do what you are doing with the action filter, anyway).
Option 2
You could use VaryByCustom and GetVaryByCustomString to vary the cache by hostname.
[SiteIDs, OutputCache(VaryByParam = "none", VaryByCustom = "hostname")]
public ActionResult SiteContent(string myID)
{
return Content(myID);
}
And in your Global.asax file:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "hostname")
{
return "HostName=" + context.Request.Url.Host;
}
return base.GetVaryByCustomString(context, custom);
}
}
Keep in mind your action filter will only be hit if the OutputCache has not been set. So you need to vary the cache on the same value (or values) that you vary your ID from. The simplest solution is to use something that is already in HttpContext, such as host name.

Specific TableController name not working

I have an extremely odd error and wondered if anyone knew the reason for this.
When I create a new DataObject and TableController called Content and ContentController respectively, it doesn't register the tablecontroller and the help documentation it automatically generates has lost its styling.
I can't connect to the controller at all but all other controllers work as expected.
If I just rename it to DataController and that's just the name of the controller, not the dataobject everything works perfectly.
Is ContentController a reserved word of some kind or is this just specifically happening on my machine?
public class DataController : TableController<Content>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileContext context = new MobileContext();
DomainManager = new EntityDomainManager<Content>(context, Request, Services);
}
// GET tables/Content
public IQueryable<Content> GetAllContent()
{
return Query();
}
// GET tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Content> GetContent(string id)
{
return Lookup(id);
}
// PATCH tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Content> PatchContent(string id, Delta<Content> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<IHttpActionResult> PostContent(Content item)
{
Content current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteContent(string id)
{
return DeleteAsync(id);
}
}
An MVC project will create an application directory called Content. This will override your route mapping to the ContentController.
You can get around this if desired through changing RouteMaps and other trickery although probably the simpliest answer is to change the name of the controller...

Ninject 2.0 InRequestScope() causing me problems - dependencies not being disposed

I'm using Ninject 2.0 with an MVC 2/EF 4 project in order to inject my repositories into my controllers. I've read that when doing something like that, one should bind using InRequestScope(). When I do that, I get a new repository per request, but the old repositories aren't being disposed. Since the old repositories are remaining in memory, I get conflicts with multiple ObjectContexts existing at the same time.
My concrete repositories implement IDisposable:
public class HGGameRepository : IGameRepository, IDisposable
{
// ...
public void Dispose()
{
if (this._siteDB != null)
{
this._siteDB.Dispose();
}
}
}
And my Ninject code:
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel kernel = new StandardKernel(new HandiGamerServices());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
{
return base.GetControllerInstance(requestContext, controllerType);
// return null;
}
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == 404)
{
IController errorController = kernel.Get<ErrorController>();
((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);
return errorController;
}
else
{
throw ex;
}
}
return (IController)kernel.Get(controllerType);
}
private class HandiGamerServices : NinjectModule
{
public override void Load()
{
Bind<HGEntities>().ToSelf().InRequestScope();
Bind<IArticleRepository>().To<HGArticleRepository>().InRequestScope();
Bind<IGameRepository>().To<HGGameRepository>().InRequestScope();
Bind<INewsRepository>().To<HGNewsRepository>().InRequestScope();
Bind<ErrorController>().ToSelf().InRequestScope();
}
}
}
What am I doing wrong?
I'm quite sure that you are wrong about the guess that your objects are not disposed. This just does not happen when you think it will happen. But the fact that this does happen later should not give you any problems with ObjectContexts unless you are doing something wrong. With a high load you will have a lot of ObjectContexts at the same time anyway.
What can become a problem though is that the memory usage increases. That's why the request scope needs to be released actively. The Ninject MVC extensions will take care of that. Otherwise have a look at the OnePerRequestModule to see how it is done:
https://github.com/ninject/Ninject.Web.Common/blob/master/src/Ninject.Web.Common/OnePerRequestHttpModule.cs

RIA Custom Update method

Given my RIA Service:
[Update]
public void Update(Car car)
{
_carRepository.Update(car);
}
[Update(UsingCustomMethod = true)]
public void UpdateAndClone(Car car)
{
_carRepository.UpdateAndClone(car);
}
How may I "map" SubmitChanges to my Custom update method from my Silverlight client side?
Public void Save(Action<SubmitOperation> submitCallback, object state)
{
_carContext.SubmitChanges(submitCallback, state);
}
Public void SaveAndClone(Action<SubmitOperation> submitCallback, object state)
{
_carContext.SubmitChanges(submitCallback, state);
// _carContext.UpdateAndClone(????)
}
I would like my application to handle the update in two quite different ways according to which action is peformed by the user, but I'm having trouble to understand how my "custom" update should be used.
It seems that I can't have both a "standard" update and a custom update.
So by calling the custom method in the viewmodel before doing .SubmitChanges() solves this issue.

Resources