WEB API validation class using attributes - c#

I have been searching google whole day and I cannot find the answer:
the problem is to validate a class with the attributes when I create a class.
So it goes: I read a POST request body and each field should be validated.
It's deserialized from Json to Request class. Then this class has its requirements.
Is it possible to do this in asp.net core using attributes?
As far as I know there are 2 ways to check class: using ValidationAttribute and Attribute inheritance.
I could swear that some time ago I was able to debug it and go to Validation class, but now it seems that is only regarding some client validation and it does not validate in backend middleware.
The last thing I am trying is using Validator.TryValidateObject.
Is it better option?

I could swear that some time ago I was able to debug it and go to Validation class, but now it seems that is only regarding some client validation and it does not validate in backend middleware.
Be sure that your razor view does not add jquery.validate.min.js and jquery.validate.unobtrusive.min.js.
For using asp.net core default template,you could just avoid using the following code in your razor view:
#*#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}*#
Then add the following code to your backend:
if (!ModelState.IsValid)
{
return View(model);
}
Update:
If you want to validate before controller,I suggest that you could custom ActionFilter.Here is a working demo with asp.net core 3.1 web api:
Model:
public class Test
{
[Range(1,4)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
Custom ActionFilter:
public class ValidationFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new JsonResult(context.ModelState.Select(m=>m.Value).ToList())
{
StatusCode = 400
};
}
}
}
Controller:
[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
// POST api/<controller>
[HttpPost]
public void Post(Test test)
{
}
}
Register in Startup.cs:
services.AddControllers(config =>
{
config.Filters.Add(new ValidationFilter());
});
Result:

Related

Razor Pages - Trying to Implement Action Filter on Razor Page

I want to write a custom filter which will check whether a user is logged in to my site, and redirect them back to the login page if they aren't.
I want the filter to apply automatically to the page when it loads.
I have tried the solution shown below, but the filter doesn't work at the moment.
Filter Code:
using Microsoft.AspNetCore.Mvc.Filters;
namespace MODS.Filters
{
public class AuthorisationPageFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
System.Diagnostics.Debug.Write("Filter Executed"); //write to debugger to test if working
//add real code here
base.OnActionExecuted(context);
}
}
}
Next, here's the filter attribute applied to the page model:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using MODS.Filters;
namespace MODS.Pages.Menus
{
[AuthorisationPageFilter]
public class Admin_MainMenuModel : PageModel
{
public ActionResult Admin_MainMenu()
{
System.Diagnostics.Debug.Write("Function Executed");
return new ViewResult();
}
}
}
I am under the impression that you need to call an action/method on the page for the function to apply when the page loads (please tell me if this is correct), so here is the code calling the Admin_MainMenu method in the .cshtml page file (in a code block at the top of the razor page):
Model.Admin_MainMenu();
My current thoughts are that either:
1. the filter itself is of the wrong type (could be IPageFilter instead?)
2. that the way I'm implementing it is wrong (either where I apply it to the
page model, or when I call the method on the page).
Any help is greatly appreciated. Thanks.
ActionFilterAttribute is for MVC (Controllers and Actions). For Razor Pages, you must use IPageFilter (IAsyncPageFilter for async implementation).
There are two different filter pipelines for MVC and Razor Pages
Razor Page filters IPageFilter and IAsyncPageFilter allow Razor Pages to run code before and after a Razor Page handler is run. Razor Page filters are similar to ASP.NET Core MVC action filters, except they can't be applied to individual page handler methods.
Filter methods for Razor Pages in ASP.NET Core
It's just as simple as the ActionFilterAttribute. All you need is to create an Attribute derived class that implements either IPageFilter or IAsyncPageFilter (Both would also work).
[AttributeUsage(AttributeTargets.Class)]
public class CustomPageFilterAttribute : Attribute, IAsyncPageFilter
{
// Executes first
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{
// TODO: implement this
}
// Executes last
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
// Before action execution
await next();
// After action execution
}
}
Now, you can use your attribute in your PageModel.
[CustomPageFilter]
public class IndexModel : PageModel
{
public void OnGet() { }
}
This answer is for AspNet MVC rather than AspNetCore MVC, but may be useful to someone:
If it's for Authorization, I would use the AuthorizeAttribute class.
Something like this:
using System.Web.Mvc;
namespace MODS.Filters
{
public class CustomAuthorizeUserAttribute : AuthorizeAttribute
{
// Custom property, such as Admin|User|Anon
public string AccessLevel { get; set; }
// Check to see it the user is authorized
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
System.Diagnostics.Debug.Write("Authorize Executed"); //write to debugger to test if working
// Use Core MVC Security Model
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false;
}
// Or use your own method of checking that the user is logged in and authorized. Returns a Boolean value.
return MySecurityHelper.CheckAccessLevel(AccessLevel);
}
// What to do when not authorized
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Error",
action = "NotFound"
})
);
}
}
}
Then Decorate the Controller or Action with the CustomAuthorizeUser Attribute:
using MODS.Filters;
namespace MODS.Pages.Menus
{
[CustomAuthorizeUser(AccessLevel = "Admin")]
public class Admin_MainMenuModel : PageModel
{
public ActionResult Admin_MainMenu()
{
System.Diagnostics.Debug.Write("Function Executed");
return new ViewResult();
}
}
}
Hope this helps!

How to get absolute URL's Path in ASP.NET Core 2.0?

I am developing an application ASP.NET Core 2.0. i have an attribute class to validation my each api request, which inherit ActionFilterAttribute class. But problem is i am not getting desired request url, the given url is coming with parameter, i need only absoluteUrl like /api/values/get not like /api/values/get/1. i think you'll get clear understanding if you see my below codes.
Api
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet("get/{id}"),AuthorizationRequiredAttribute]
public IEnumerable<string> Get(int id)
{
return id;
}
}
AuthorizationRequiredAttribute
public class AuthorizationRequiredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var url=context.HttpContext.Request.Path.ToString()
}
}
Take a look at Request.GetDisplayURL. This method will give complete URL. Note that the parameter is part of the URL initiated from the client so it will be shown. You will either need to remove it manually or change your URL scheme to make use of query strings.
.Contains() method help me to implement logic with given action url
public class AuthorizationRequiredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var url=context.HttpContext.Request.Path.ToString()
if(url.Contains("/api/values/get/1"))
{
//do something
}
}
}

Non-attribute routes in ASP.Net Core WebApi

I need to build project, that implement REST API predefined by vendor application(which will consume it) - there is about thousand of REST-resources with some actions defined by different HTTP-Verb's(POST, GET, PUT, DELETE, etc..).
So, ideally, for each resource i should have single class like this:
public class SomethingController
{
public Something Post(string name, DateTime time)
{
// ...
}
public int PostStrange(string text)
{
// ...
}
public Something Put([FromBody]Something item)
{
// ...
}
public void Delete(int id)
{
// ...
}
}
In previous versions i can just call MapHttpRoute while registering routes, inherit classes like this from ApiController - and ASP.NET Web Api will do as i need... But in .NET Core i can't find anything like MapHttpRoute/ApiController.. Now there is routing and http-verb attributes, and i need to define everything explicitly for each class/method:
[Route("api/[controller]")]
public class SomethingController : Controller
{
[HttpPost]
public Something Post(string name, DateTime time)
{
// ...
}
[HttpPost("api/[controller]/strange")]
public int PostStrange(string text)
{
// ...
}
[HttpPut]
public Something Put([FromBody]Something item)
{
// ...
}
[HttpDelete]
public void Delete(int id)
{
// ...
}
}
Writing this attributes for each of thousands REST-resources is very boring and error prone...
Do i miss something here? Why in pretty new and modern ASP.NET Core that very common and important thing as building REST-Api made so over-complicated, compared to old ASP.NET?
There is nuget package Microsoft.AspNetCore.Mvc.WebApiCompatShim which main goal is to make migration from web api to core easier. It also provides a way to perform convention-based routing to actions you need. So, first install that package, then in startup:
public void ConfigureServices(IServiceCollection services) {
// add conventions here
services.AddMvc().AddWebApiConventions();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseMvc(routes => {
// map one global route
routes.MapWebApiRoute("WebApi", "api/{controller}");
});
}
After this small configuration you can inherit your controllers either from ApiController, which is added in package above for convenience of migration from web api, or native asp.net core Controller. Example of ApiController:
public class SomeController : ApiController {
// maps to GET /api/Some
// note - no routing attributes anywhere
public HttpResponseMessage Get() {
return new HttpResponseMessage(HttpStatusCode.OK);
}
// maps to POST /api/Some
public HttpResponseMessage Post() {
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
Native asp.net core controller:
// mark with these attributes for it to work
[UseWebApiRoutes]
[UseWebApiActionConventions]
public class TestController : Controller {
// maps to GET /api/Test
// no routing attributes, but two "conventions" attributes
public IActionResult Get(string p) {
return new ObjectResult(new { Test = p });
}
}
You can also mark your base controller with these attributes:
[UseWebApiRoutes]
[UseWebApiActionConventions]
public class BaseController : Controller {
}
public class TestController : BaseController {
// maps to GET /api/Test
// no attributes
public IActionResult Get(string p) {
return new ObjectResult(new { Test = p });
}
}
If you are not migrating from web api - I'd suggest to use native Controller. ApiController has different structure (similar to asp.net web api ApiController), so there is not much reason to use it for anything other than its intended goal (migration from web api).
MapRoute is still there https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing
Attribute routing compliments MapRoute, not replaces it.
Apparently there are quite a few examples which drop the piece about Routing in order to simplify example. So just dig dipper.

Multiple controllers with same URL routes but different HTTP methods

I've got a following two controllers:
[RoutePrefix("/some-resources")
class CreationController : ApiController
{
[HttpPost, Route]
public ... CreateResource(CreateData input)
{
// ...
}
}
[RoutePrefix("/some-resources")
class DisplayController : ApiController
{
[HttpGet, Route]
public ... ListAllResources()
{
// ...
}
[HttpGet, Route("{publicKey:guid}"]
public ... ShowSingleResource(Guid publicKey)
{
// ...
}
}
All three actions got in fact three different routes:
GET /some-resources
POST /some-resources
GET /some-resources/aaaaa-bbb-ccc-dddd
If I put them into single controller everything works just fine, however if I separate them (as shown above) WebApi throws following exception:
Multiple controller types were found that match the URL. This can
happen if attribute routes on multiple controllers match the requested
URL.
This message is quite obvious. It seems WebApi does not take HTTP method into account when looking for a right candidate for controller/action.
How could I achieve the expected behavior?
UPDATE: I've digged a little into Web API internals and I understand that's the way it works by default. My goal is to separate the code and logic - in real world case those controllers have different dependencies and are a bit more complex. For the sake of maintenance, testability, project organization etc. they should be different objects (SOLID and stuff).
I thought I could override some WebAPI services (IControllerSelector etc) however this seems to be a little bit risky and non-standard approach for this simple and - as I assumed - common case.
UPDATE
Based on your comments, updated question and the answer provided here
Multiple Controller Types with same Route prefix ASP.NET Web Api
Desired result can be achieved via custom route constraints for the HTTP method applied to controller actions.
On inspection of the default Http{Verb} attributes ie [HttpGet], [HttpPost] and the RouteAttribute, which by the way are sealed, I realized that their functionality can be combine into one class similar to how they are implemented in Asp.Net-Core.
The following is for GET and POST, but it shouldn't be difficult to create constraints for the other HTTP methods PUT, DELETE...etc to be applied to the controllers.
class HttpGetAttribute : MethodConstraintedRouteAttribute {
public HttpGetAttribute(string template) : base(template, HttpMethod.Get) { }
}
class HttpPostAttribute : MethodConstraintedRouteAttribute {
public HttpPostAttribute(string template) : base(template, HttpMethod.Post) { }
}
The important class is the route factory and the constraint itself. The framework already has base classes that take care of most of the route factory work and also a HttpMethodConstraint so it is just a matter of applying the desired routing functionality.
class MethodConstraintedRouteAttribute
: RouteFactoryAttribute, IActionHttpMethodProvider, IHttpRouteInfoProvider {
public MethodConstraintedRouteAttribute(string template, HttpMethod method)
: base(template) {
HttpMethods = new Collection<HttpMethod>(){
method
};
}
public Collection<HttpMethod> HttpMethods { get; private set; }
public override IDictionary<string, object> Constraints {
get {
var constraints = new HttpRouteValueDictionary();
constraints.Add("method", new HttpMethodConstraint(HttpMethods.ToArray()));
return constraints;
}
}
}
So given the following controller with the custom route constraints applied...
[RoutePrefix("api/some-resources")]
public class CreationController : ApiController {
[HttpPost("")]
public IHttpActionResult CreateResource(CreateData input) {
return Ok();
}
}
[RoutePrefix("api/some-resources")]
public class DisplayController : ApiController {
[HttpGet("")]
public IHttpActionResult ListAllResources() {
return Ok();
}
[HttpGet("{publicKey:guid}")]
public IHttpActionResult ShowSingleResource(Guid publicKey) {
return Ok();
}
}
Did an in-memory unit test to confirm functionality and it worked.
[TestClass]
public class WebApiRouteTests {
[TestMethod]
public async Task Multiple_controllers_with_same_URL_routes_but_different_HTTP_methods() {
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
var errorHandler = config.Services.GetExceptionHandler();
var handlerMock = new Mock<IExceptionHandler>();
handlerMock
.Setup(m => m.HandleAsync(It.IsAny<ExceptionHandlerContext>(), It.IsAny<System.Threading.CancellationToken>()))
.Callback<ExceptionHandlerContext, CancellationToken>((context, token) => {
var innerException = context.ExceptionContext.Exception;
Assert.Fail(innerException.Message);
});
config.Services.Replace(typeof(IExceptionHandler), handlerMock.Object);
using (var server = new HttpTestServer(config)) {
string url = "http://localhost/api/some-resources/";
var client = server.CreateClient();
client.BaseAddress = new Uri(url);
using (var response = await client.GetAsync("")) {
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
using (var response = await client.GetAsync("3D6BDC0A-B539-4EBF-83AD-2FF5E958AFC3")) {
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
using (var response = await client.PostAsJsonAsync("", new CreateData())) {
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
}
public class CreateData { }
}
ORIGINAL ANSWER
Referencing : Routing and Action Selection in ASP.NET Web API
That's because it uses the routes in the route table to find the controller first and then checks for Http{Verb} to select an action. which is why it works when they are all in the same controller. if it finds the same route to two different controllers it doesn't know when one to select, hence the error.
If the goal is simple code organization then take advantage of partial classes
ResourcesController.cs
[RoutePrefix("/some-resources")]
partial class ResourcesController : ApiController { }
ResourcesController_Creation.cs
partial class ResourcesController {
[HttpPost, Route]
public ... CreateResource(CreateData input) {
// ...
}
}
ResourcesController_Display.cs
partial class ResourcesController {
[HttpGet, Route]
public ... ListAllResources() {
// ...
}
[HttpGet, Route("{publicKey:guid}"]
public ... ShowSingleResource(Guid publicKey) {
// ...
}
}

Why AntiXss library is not supported for Web Api the same way as MVC does?

According to Microsoft starting from .NET 4.0 the AntiXSS is included in the framework and enabled by default.
this is true for MVC but when it comes to Web Api there is no proper solution.
for example I have this very basic API flow:
public class MessageViewModel
{
public int TargetID { get; set; }
public string Content { get; set; }
}
and the Api Controller:
public class MessagesController : ApiController
{
[HttpPost]
public MessageResponse SendMessage(MessageViewModel model)
{
MessageResponse response = null;
if (ModelState.IsValid)
{
response = _logic.SendMessage(model);
}
return response;
}
}
this Content property of the MessageViewModel object may contains malicious scripts/code that later on will be sent to another users.
is there a way to enable the AntiXSS on Web Api the same way it works for MVC? and if not, what proper solution exist to protect the entire application from XSS without applying attributes on each specific view model?

Categories

Resources