I have a very dumb issue related to a custom formatter.
public class RequestHeaderJsonFormatter : MediaTypeFormatter
{
public RequestHeaderJsonFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
}
public override System.Threading.Tasks.Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
return base.ReadFromStreamAsync(type, readStream, content, formatterLogger);
}
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return true;
}
}
I register the formatter in Global.asax as it follows:
protected void Application_Start()
{
GlobalConfiguration.Configuration.Formatters.Insert(0, new RequestHeaderJsonFormatter());
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}
My issue is that ReadFromStreamAsync never gets called when executing the controller action.
public class HomeController : ApiController
{
public HttpResponseMessage GetString(string param)
{
var response = Request.CreateResponse(HttpStatusCode.OK, "ererrer");
return response;
}
}
What am I doing wrong...I can't figure it out. Any help would be much appreciated.
By default 'string' type action parameters are expected to be read from Uri, unless [FromBody] attribute is used to decorate it, in which case the formatters come into picture.
public HttpResponseMessage GetString(*[FromBody]*string param)
Related
This is my action the ModelState checks only the bookId parameter. The other one even if it is null, no error is raised.
Is there any way to make it check the ModelState of all parameters?
[HttpPut]
[Route("{bookId}")]
public IHttpActionResult Edit([FromBody] EditBookBindingModel model, int bookId)
{
if (!this.service.ExistsBook(bookId))
{
return this.NotFound();
}
if (!this.ModelState.IsValid)
{
return this.StatusCode(HttpStatusCode.BadRequest);
}
this.service.EditBook(bookId, model);
return this.Ok();
}
You could define an ActionFilterAttribute that protects you from null arguments:
public class CheckModelForNullAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionArguments.ContainsValue(null))
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "The argument cannot be null");
}
}
Then use this:
[HttpPut]
[Route("{bookId}")]
[CheckModelForNull]
public IHttpActionResult Edit([FromBody] EditBookBindingModel model, int bookId)
{
// model canĀ“t be null now
...
I wrote a custom filter so DataAnnotations works with parameters also.
Here is the filter.
public class ModelValidationFilter : FilterAttribute, IActionFilter, IFilter
{
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext,
CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
var parameters = actionContext.ActionDescriptor.GetParameters();
if (parameters.Any())
{
var validationParams = parameters.Where(x => x.GetCustomAttributes<ValidationAttribute>().Any());
if (validationParams.Any())
{
foreach (var item in validationParams)
{
var val = actionContext.ActionArguments[item.ParameterName];
foreach (var attr in item.GetCustomAttributes<ValidationAttribute>())
{
if (!attr.IsValid(val))
{
actionContext.ModelState.AddModelError(item.ParameterName, attr.FormatErrorMessage(item.ParameterName));
}
}
}
}
if (!actionContext.ModelState.IsValid)
{
return Task.FromResult(actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState));
}
}
return continuation();
}
}
Usage (I have'nt tested completely.):
add it to global filters.
config.Filters.Add(new ModelValidationFilter());
public Student Post([Required] addStudentDTO)
{
//Your logic
}
public Student Patch([FromBody,Required] addStudentDTO, [Required,EmailAddress]string emailAddress])
{
//Your logic
}
I'm pulling a razor view's markup from the database, as detailed in this question:
ASP.NET MVC load Razor view from database
I can pull the view, but it fails on execution because ViewBag is not recognized.
CS0103: The name 'ViewBag' does not exist in the current context
Any suggestions?
Here's the source:
global:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new BearForce.Web.Core.DbPathProvider());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
my path provider:
namespace BearForce.Web.Core
{
public class DbPathProvider : VirtualPathProvider
{
public DbPathProvider()
: base()
{
}
public override bool FileExists(string virtualPath)
{
var repo = new Repository();
var viewPage = repo.GetView(240, virtualPath);
if (base.FileExists(virtualPath))
{
return true;
}
if (viewPage != null)
{
return true;
}
return false;
}
public override VirtualFile GetFile(string virtualPath)
{
if (base.FileExists(virtualPath))
{
return base.GetFile(virtualPath);
}
var repo = new Repository();
var result = repo.GetView(240, virtualPath);
var vf = new DbVirtualFile(virtualPath, result.Markup);
return vf;
}
}
}
my Virtual File:
public class DbVirtualFile : System.Web.Hosting.VirtualFile
{
string _fileContents = string.Empty;
public DbVirtualFile(string path, string fileContents)
: base(path)
{
_fileContents = fileContents;
}
public override System.IO.Stream Open()
{
return new System.IO.MemoryStream(System.Text.ASCIIEncoding.ASCII.GetBytes(_fileContents));
}
}
My Controller:
public ActionResult Index()
{
ViewBag.Title = "aaah!!! Muppets!!! Help!!!!!";
return View();
}
Obviously, this is a proof of concept, so the names are all silly and the code sloppy as hell...
For future people who get this error, you can get this exact error if your web.config files are missing from your Views and your root project folder.
You should make sure that the view you are returning corresponds to a razor view. Here's a simplified working example:
public class CustomPathProvider : VirtualPathProvider
{
private class CustomVirtualFile : VirtualFile
{
public CustomVirtualFile(string path)
: base(path)
{ }
public override Stream Open()
{
return new MemoryStream(Encoding.UTF8.GetBytes("Hello #ViewBag.Name"));
}
}
public override bool FileExists(string virtualPath)
{
// This is very important: make sure that here you
// are returning true only for Razor view pages or
// you won't have ViewBag.
// In this oversimplified example we support
// the index view for the home controller
return virtualPath == "/Views/Home/Index.cshtml";
}
public override VirtualFile GetFile(string virtualPath)
{
return new CustomVirtualFile(virtualPath);
}
}
which would be registered in Application_Start:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
HostingEnvironment.RegisterVirtualPathProvider(new CustomPathProvider());
}
and finally a sample controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Name = "John";
return View();
}
}
And a final very important remark if you are implementing a custom VirtualPathProvider:
This doesn't work if your web application is precompiled. So if you are using precompilation (things like Publish... or Web Deployment Projects) your custom virtual path provider will never be used.
As part of a identity-enabled authorization system, I'd like to use IAuhtorizationFilter and Attributes to restrict access to action methods in my controllers. I've got things working very well, partly due to help from the following resources:
Ninject Binding Attribute to Filter with Constructor Arguments
https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations
Custom Authorization MVC 3 and Ninject IoC
https://github.com/ninject/ninject.web.mvc/wiki/Dependency-injection-for-filters
However, when I try to decorate an action method with more than one of my attributes, I get an exception as follows (sorry for the formatting):
[InvalidOperationException: Sequence contains more than one element]
System.Linq.Enumerable.Single(IEnumerable`1 source) +2691369
Ninject.Web.Mvc.FilterBindingSyntax.c__DisplayClass15`1.b__14(IContext ctx, ControllerContext controllerContext, ActionDescriptor actionDescriptor) in c:\Projects\Ninject\Maintenance2.2\ninject.web.mvc\mvc3\src\Ninject.Web.Mvc\FilterBindingSyntax\FilterFilterBindingBuilder.cs:379
Ninject.Web.Mvc.FilterBindingSyntax.c__DisplayClass12.b__11(IContext ctx) in c:\Projects\Ninject\Maintenance2.2\ninject.web.mvc\mvc3\src\Ninject.Web.Mvc\FilterBindingSyntax\FilterFilterBindingBuilder.cs:358
Ninject.Parameters.c__DisplayClass6.b__4(IContext ctx, ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Parameters\Parameter.cs:60
Ninject.Parameters.Parameter.GetValue(IContext context, ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Parameters\Parameter.cs:88
Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:97
Ninject.Activation.Providers.c__DisplayClass2.b__1(ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:81
...
Here is a very simplified version of my code that demonstrates the problem in an MVC3 app:
Attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class SampleAttribute : Attribute
{
private Guid typeId;
public bool IsAllowed { get; set; }
public SampleAttribute(bool IsAllowed)
{
this.IsAllowed = IsAllowed;
this.typeId = new Guid();
}
public override object TypeId
{
get
{
return (object)typeId;
}
}
}
Filter:
public class SampleFilter : IAuthorizationFilter, IMvcFilter
{
private bool isAllowed;
public bool AllowMultiple
{
get { return true; }
}
public int Order
{
get { return 0; }
}
public SampleFilter(bool isAllowed)
{
this.isAllowed = isAllowed;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
if (!isAllowed)
throw new Exception("unauthorized");
}
}
Controller:
public class HomeController : Controller
{
[Sample(true)]
[Sample(false)]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
The controller method above works as expected if one or the other of the Sample attributes on the Index() method is removed. Having both in place, generates the exception. I realize that in this simplified example, there isn't a situation that would call for both attributes, but it's simply for illustration.
What am I missing?
This is a known issue of Ninject 2.2. Please use 3.0 instead.
https://github.com/ninject/ninject.web.mvc/blob/master/mvc3/ReleaseNotes.txt
Is there a quick method for intercepting all controller calls in MVC-3?
For logging and testing purposes, I'd like to build a tool that can intercept all controller calls, and log which controller was called, with which message, at what time.
I can't remember where I got this from, but I was looking around for something similar a while back and found an article or something somewhere that contained this logging filter:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = string.Format("{0} controller: {1} action: {2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
To use it, just add it to the global filters in global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new LogActionFilter());
}
I'll have a look now to see if I can find the source.
Edit: Found it. It was from this question.
Depending on how big the site is already, you could create a class in the hierarchy between the framework's Controller class and your main controllers.
Something like
public class MyBaseController : Controller {
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
// your logging stuff here
base.OnActionExecuting(filtercontext);
}
}
Then the rest of your controllers can inherit from this, e.g.
public class HomeController : MyBaseController {
// action methods...
}
You can use your own controller factory and register it as well:
From: (many example on the net - insert logging where you want)
adapted from: http://www.keyvan.ms/custom-controller-factory-in-asp-net-mvc
using System;
using System.Configuration;
using System.Web.Mvc;
using System.Web.Routing;
namespace IControllerFactorySample.ControllerFactories
{
public class YourControllerFactory : IControllerFactory
{
#region IControllerFactory Members
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
IController controller = Activator.CreateInstance(Type.GetType(controllerName)) as IController;
return controller;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
(controller as IDisposable).Dispose();
else
controller = null;
}
#endregion
}
}
dont forget to register it in global.asax.cs
ControllerBuilder.Current.SetControllerFactory(
typeof(YourControllerFactory));
there is a routing debugger developed by Phil Haack
ASP.Net Routing Debugger
I was thinking how to correctly secure JsonResult action with custom attribute instead of doing kind of this on each action like saying here ASP.NET MVC JsonResult and AuthorizeAttribute
if (!User.Identity.IsAuthenticated)
return Json("Need to login");
But the question is how could i create such attribute which would return Json.
So i've started from that:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class JsonAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
//?
}
//Need to return json somehow ?
}
}
Bot how i may return json result from such attribute? any ideas?
You can use an ActionFilterAttribute which allows you to return a result without using the httpcontext.response.write or anything.
public class JsonActionFilterAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (!HttpContext.Current.User.Identity.IsAuthenticated) {
filterContext.Result = new JsonResult() { Data = "Need to login." };
}
base.OnActionExecuting(filterContext);
}
}
1 way is to override AuthorizeAttribute.HandleUnauthorizedRequest
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
throw new CustomUnauthorizedException();
}
... And then in your Global.asax:
protected void Application_Error(object sender, EventArgs e)
{
Exception error = Server.GetLastError();
if (error is CustomUnauthorizedException) {
if (AjaxRequest(Request)) {
... return Json response.
} else {
... redirect
}
}
}
So you can throw the exception anywhere in your codebase and you've centralized the handling of that exception in Global.asax
Try this.. it works for me
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
dynamic ResponseObj = new JObject();
ResponseObj.Message = "Authorization has been denied for this request.";
string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(ResponseObj);
actionContext.Response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.Unauthorized,
Content = new StringContent(jsonString, System.Text.Encoding.UTF8,"application/json")
};
}