Same Policy but different required parameter for each action method - asp.net-core-webapi

In a .Net core Webapi 2.1 project, I have a tons of action methods.
All action methods should be authorized against the same policy (named FooPolicy) but with a different required argument.
Based on Microsoft's docs: Policy-based-Authorization
One way would be to declare a tons of policies based on different input argument:
services.AddAuthorization(options =>
{
options.AddPolicy("FooPolicy1", policy =>policy.Requirements.Add(new FooRequirement(1)));
options.AddPolicy("FooPolicy2", policy =>policy.Requirements.Add(new FooRequirement(2)));
options.AddPolicy("FooPolicy3", policy =>policy.Requirements.Add(new FooRequirement(3)));
//... May be 30 more same policies here ...
});
As i earlier mentioned, only different part is in new FooRequirement(diffArgs). The other challenge for this solution would be to add each FooPolicy on it's corresponding action method and you may miss a couple of theme:
[Authorize(Policy = "FooPolicy1")]
public IActionResult ActionMethodFoo1(...) {...}
[Authorize(Policy = "FooPolicy2")]
public IActionResult ActionMethodFoo2(...) {...}
[Authorize(Policy = "FooPolicy3")]
public IActionResult ActionMethodFoo3(...) {...}
...List still goes on...
Is there any solution like: declare a policy once but use it with different instance of FooRequirement (which is of type IAuthorizationHandler)? like so:
services.AddAuthorization(options =>
{
options.AddPolicy("FooPolicy", policy =>policy.Requirements.Add(?));
});
And on the action methods:
[Authorize(Policy = "FooPolicy", required = new FooRequirement(1))]
public IActionResult ActionMethodFoo1(...) {...}
[Authorize(Policy = "FooPolicy", required = new FooRequirement(2))]
public IActionResult ActionMethodFoo2(...) {...}
[Authorize(Policy = "FooPolicy", required = new FooRequirement(3))]
public IActionResult ActionMethodFoo3(...) {...}
The main idea is to declare policy once. Two recent code blocks are psudo-code, Does any body knows practical solution with similar concept?

You could implement your own IAuthorizationFilter
custom IAuthorizationFilter
public class CustomAuthorize : IAuthorizationFilter
{
private readonly int _input;
public CustomAuthorize(int input)
{
_input = input;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//custom validation rule
if (_input == 1)
{
context.Result = new ForbidResult();
}
}
}
Custom CustomAuthorizeAttribute
public class CustomAuthorizeAttribute : TypeFilterAttribute
{
public CustomAuthorizeAttribute(int input) : base(typeof(CustomAuthorize))
{
Arguments = new object[] { input };
}
}
Use
[CustomAuthorizeAttribute(1)]
public IActionResult About()

Related

Request cannot be converted while using AbstractValidator as a base class

My project throws a convert error when I use fluentvalidation class as a request model.
I have a BaseRequest. It is inherited from AbstractValidatator
public abstract class BaseRequest<TModel> : AbstractValidator<TModel>
{
}
Another model is CreateRuleRequest and it is inherited from BaseRequest. And also it includes some rule for its properties.
public class CreateRuleRequest : BaseRequest<CreateRuleRequest>
{
public CreateRuleRequest()
{
this.RuleFor(k => k.Rules).NotEmpty();
}
public List<RuleModel> Rules { get; set; }
}
And I am using an Extension to load all rules that are inherited from BaseRequest. Actually, AbstractValidator extends from IValidator so that I can catch all the classes that includes rules.
public static IServiceCollection AddValidation(this IServiceCollection services)
{
var validators = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s =>
s.GetTypes())
.Where(t => t.GetInterfaces().Contains(typeof(IValidator))).Where(k =>
k.GetInterfaces().Contains(typeof(IQuery)) ||
k.GetInterfaces().Contains(typeof(ICommand)));
foreach (var validator in validators)
{
services.AddValidatorsFromAssemblyContaining(validator);
}
return services;
}
Lastly, I have a controller that has a Create method. This method is using CreateRuleRequest as a parameter.
public async Task<IActionResult> Create([FromBody] CreateRuleRequest command) =>
and the application throws The JSON value could not be converted CreateRuleRequest. I am wondering why? is it mandatory to create another model binder or is there any other way

Return correct 201 when using controller actions attribute routing

Net core 3 web api. It uses attribute routing for controllers actions. Few separate controllers with post methods (e.g. CreatedContract) should return 201 where routeName related to : GetOperationsById. Example is very simplified comparing to actual app, so please lets not put to much attention to it style
How to set route name to CreatedAtRoute correctly:
[ApiController]
public class OperationsController : ControllerBase
{
[HttpGet]
[Route("contractsOperation/{id}")]
public async Task<IActionResult> GetOperationsById(int id)
{
var operation = _service.GetOperation(id);
return Ok(operation);
}
}
[ApiController]
public class ContractsController : ControllerBase
{
[HttpPost]
[Route("saveContract")]
public async Task<IActionResult> CreatedContract(string jsonData)
{
var newlyCreatedContract = _service.Create(jsonData);
//var route = this value should point out to OperationsController -> GetOperationsById
return CreatedAtRoute(route, new { newlyCreatedContract.Id }, newlyCreatedContract);
}
}
Thank you
According to your description, I suggest you could try to add a route name to the GetOperationsById route attribute and then use this attribute name as the parameter to the CreatedAtRoute method.
More details, you could refer to below example:
Other controller method:
[HttpGet]
[Route("contractsOperation/{id}", Name = nameof(GetOperationsById))]
public async Task<IActionResult> GetOperationsById(int id)
{
return Ok("aa");
}
Call it:
[HttpGet]
public IActionResult Index()
{
return CreatedAtRoute(nameof(FooController.GetOperationsById), new { id = 1 }, new { id=1});
}
Result:

ASP.NET Web API Contract Versioning

We would like to achieve version based API using content negotiation in accept header.
We are able to achieve for controller & API methods with some inheritance and extending the default HTTP selector.
Controller inheritance is achieved using following sample code,
public abstract class AbstractBaseController : ApiController
{
// common methods for all api
}
public abstract class AbstractStudentController : AbstractBaseController
{
// common methods for Student related API'sample
public abstract Post(Student student);
public abstract Patch(Student student);
}
public class StudentV1Controller : AbstractStudentController
{
public override Post([FromBody]Student student) // student should be instance of StudentV1 from JSON
{
// To Do: Insert V1 Student
}
public override Patch([FromBody]Student student) // student should be instance of StudentV1 from JSON
{
// To Do: Patch V1 Student
}
}
public class StudentV2Controller : AbstractStudentController
{
//
public override Post([FromBody]Student student) // student should be instance of StudentV2 from JSON
{
// To Do: Insert V2 Student
}
}
public abstract class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class StudentV1 : Student
{
}
public class StudentV2 : Student
{
public string Email { get; set; }
}
We have created above architecture to do less code with change in version, say if version 1 has 10 API methods and there is a change in one API method than it should be available in version 2 code without modifying other 9(they are inherited from version 1).
Now, the main problem we are facing is in contract versioning as we cannot instantiate an instance of an abstract student. When someone is posting JSON to API version 1 instance of StudentV1 should be passed in methods and same in version 2.
Is there any way to achieve this?
Thanks in advance!!
ASP.NET API Versioning is capable of achieving your goals. First, you'll want to add a reference to the ASP.NET Web API API Versioning NuGet package.
You would then configure your application something like:
public class WebApiConfig
{
public static void Configure(HttpConfiguration config)
{
config.AddApiVersioning(
options => options.ApiVersionReader = new MediaTypeApiVersionReader());
}
}
Your controllers might look something like:
namespace MyApp.Controllers
{
namespace V1
{
[ApiVersion("1.0")]
[RoutePrefix("student")]
public class StudentController : ApiController
{
[Route("{id}", Name = "GetStudent")]
public IHttpActionResult Get(int id) =>
Ok(new Student() { Id = id });
[Route]
public IHttpActionResult Post([FromBody] Student student)
{
student.Id = 42;
var location = Link("GetStudent", new { id = student.Id });
return Created(location, student);
}
[Route("{id}")]
public IHttpActionResult Patch(int id, [FromBody] Student student) =>
Ok(student);
}
}
namespace V2
{
[ApiVersion("2.0")]
[RoutePrefix("student")]
public class StudentController : ApiController
{
[Route("{id}", Name = "GetStudentV2")]
public IHttpActionResult Get(int id) =>
Ok(new Student() { Id = id });
[Route]
public IHttpActionResult Post([FromBody] StudentV2 student)
{
student.Id = 42;
var location = Link("GetStudentV2", new { id = student.Id });
return Created(location, student);
}
[Route("{id}")]
public IHttpActionResult Patch(int id, [FromBody] StudentV2 student) =>
Ok(student);
}
}
}
I strongly advise against inheritance. It's possible, but is the wrong approach to the problem IMO. Neither APIs nor HTTP support inheritance. That is an implementation detail of the backing language, which is also somewhat of an impedance mismatch. A key problem is that you cannot uninherit a method and, hence, nor an API.
If you really insist on inheritance. Choose one of the following options:
Base class with only protected members
Move business logic out of the controllers
Use extension methods or other collaborators to fulfill shared operations
For example, you might do something like this:
namespace MyApp.Controllers
{
public abstract class StudentController<T> : ApiController
where T: Student
{
protected virtual IHttpActionResult Get(int id)
{
// common implementation
}
protected virtual IHttpActionResult Post([FromBody] T student)
{
// common implementation
}
protected virtual IHttpActionResult Patch(int id, [FromBody] Student student)
{
// common implementation
}
}
namespace V1
{
[ApiVersion("1.0")]
[RoutePrefix("student")]
public class StudentController : StudentController<Student>
{
[Route("{id}", Name = "GetStudentV1")]
public IHttpActionResult Get(int id) => base.Get(id);
[Route]
public IHttpActionResult Post([FromBody] Student student) =>
base.Post(student);
[Route("{id}")]
public IHttpActionResult Patch(int id, [FromBody] Student student) =>
base.Patch(student);
}
}
namespace V2
{
[ApiVersion("2.0")]
[RoutePrefix("student")]
public class StudentController : StudentController<StudentV2>
{
[Route("{id}", Name = "GetStudentV2")]
public IHttpActionResult Get(int id) => base.Get(id);
[Route]
public IHttpActionResult Post([FromBody] StudentV2 student) =>
base.Post(student);
[Route("{id}")]
public IHttpActionResult Patch(int id, [FromBody] StudentV2 student) =>
base.Patch(student);
}
}
}
There are other ways, but that is one example. If you define a sensible versioning policy (ex: N-2 versions), then the amount of duplication is minimal. Inheritance will likely cause more problems than it solves.
When you version by media type, the default behavior uses the v media type parameter to indicate the API version. You can change name if you wish. Other forms of versioning by media type are possible (ex: application/json+student.v1, you'd need a custom IApiVersionReader as there is no standard format. In addition, you'll have to update the ASP.NET MediaTypeFormatter mappings in the configuration. The built-in media type mapping does not consider media type parameters (e.g. the v parameter has no impact).
The following table shows the mapping:
Method
Header
Example
GET
Accept
application/json;v=1.0
PUT
Content-Type
application/json;v=1.0
POST
Content-Type
application/json;v=1.0
PATCH
Content-Type
application/json;v=1.0
DELETE
Accept or Content-Type
application/json;v=1.0
DELETE is an outlier case as it doesn't require a media type in or out. Content-Type will always take precedence over Accept because it is required for the body. A DELETE API can be made API version-neutral, meaning will take any API version, including none at all. This may be useful if you want to allow DELETE without requiring a media type. Another alternative could be to use media type and query string versioning methods. This would allow specifying the API version in the query string for DELETE APIs.
Over the wire, it will look like:
Request
POST /student HTTP/2
Host: localhost
Content-Type: application/json;v=2.0
Content-Length: 37
{"firstName":"John","lastName":"Doe"}
Response
HTTP/2 201 Created
Content-Type: application/json;v=2.0
Content-Length: 45
Location: http://localhost/student/42
{"id":42,"firstName":"John","lastName":"Doe"}
Based on your pasted code you could make AbstractStudentController generic.
Because those APIs that you declare abstract must be implemented in every API version, and you can define type with the generic. I hope I'm not missing something from your description, because Patch is missing from your implementation in StudentV2Controller, but is declared abstract. Do you wanted to derive StudentV2Controller from StudentV1Controller?
public abstract class AbstractBaseController : ApiController
{
// common methods for all api
}
public abstract class AbstractStudentController<StudentType> : AbstractBaseController
{
// common methods for Student related API'sample
public abstract Post(StudentType student);
public abstract Patch(StudentType student);
}
public class StudentV1Controller : AbstractStudentController<StudentV1>
{
public override Post([FromBody]StudentV1 student) // student should be instance of StudentV1 from JSON
{
// To Do: Insert V1 Student
}
public override Patch([FromBody]StudentV1 student) // student should be instance of StudentV1 from JSON
{
// To Do: Patch V1 Student
}
}
public class StudentV2Controller : AbstractStudentController<StudentV2>
{
//
public override Post([FromBody]StudentV2 student) // student should be instance of StudentV2 from JSON
{
// To Do: Insert V2 Student
}
}

Is there a way to get the current controller instance in ASP.NET 5?

Is there a way to do this using DI? I tried IScopedInstance<Controller> but this gives me null. Poked around aspnet's source code but didn't win. Any ideas?
I have a controller that accepts different IPaymentMethods. The IPaymentMethod can be a ViewComponent that can render Views. If the IPaymentMethod is a ViewComponent, I want it to use MVC's built-in model binding on post back.
public class XController : Controller
{
// ctor, props, ...
public IActionResult Checkout()
{
return View(new Model
{
PaymentMethodId = 1,
PaymentMethodType = typeof(MyPaymentMethod) // The razor file will use this type to render it as a ViewComponent
});
}
[HttpPost]
public IActionResult Checkout(Model model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
paymentMethod.ProcessPayment();
// ..
}
}
This is where I need the controller to be injected. I wanted to make use of the built-in MVC validation and model binding.
public class MyPaymentMethod : IPaymentMethod
{
private Controller _currentController;
public MyPaymentMethod(IScopedInstance<Controller> controller)
{
_currentController = controller.Value;
}
public void ProcessPayment()
{
var model = new PaymentModel();
_currentController.TryUpdateModel(model, typeof(PaymentModel), null);
if (!_currentController.ModelState.IsValid)
{
return; // or exception
}
// Process Payment using model
}
public Task<IViewComponentResult> InvokeAsync()
{
// returns View
}
}
public interface IPaymentMethod
{
void ProcessPayment();
}
Since the model instance is required in the ProcessPayment method, why not simply pass it as a parameter?
[HttpPost]
public IActionResult Checkout(PaymentModel model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
if (!ModelState.IsValid)
{
return; // or exception
}
paymentMethod.ProcessPayment(model);
// ..
}
public void ProcessPayment(PaymentModel model)
{
// Process Payment using model
}
Your service is taking on responsibilities that belong to the controller - namely checking ModelState.IsValid.
public interface IPaymentMethod
{
void ProcessPayment(PaymentModel model);
}
You may wish to also pass just the properties that are needed from the payment model, or you may wish to make an IPaymentModel interface to decouple your model from your PaymentService. In that case, your IPaymentModel would go into a shared layer.
public interface IPaymentMethod
{
void ProcessPayment(IPaymentModel model);
}
This no longer works with beta7
At this time of writing (beta6), this probably isn't supported and there is a good reason for it: Controllers in ASP.NET 5 does not need to inherit from the Controller class. I have, however, found a way for this to work using ActionFilters.
public class ScopeControllerActionFilterAttribute : ActionFilterAttribute
{
private readonly IScopedInstance<Controller> _controller;
public ScopeControllerActionFilterAttribute(IScopedInstance<Controller> controller)
{
_controller = controller;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (_controller.Value == null)
{
_controller.Value = context.Controller as Controller;
}
}
}
Note that depending on the stage of the http request lifecycle, the Value of IScopedInstance<Controller> may still be empty.

Asynchronous call to webservice in MVC 4 web application

I am building my first real MVC4 application and I have run into following issue.
I have a model for "User" class. Data for it are obtained through asynchronous call to webservice:
public sealed class AdminDMSEntities
{
public List<User> UserList { get; private set; }
public AdminDMSEntities()
{
this.UserList = new List<User>(0);
ServiceClient client = new ServiceClient();
client.GetUsersCompleted += (s, e) =>
{
if (e.Result == null)
throw new ArgumentNullException("No users were retrieved");
UserList = new List<User>(0);
e.Result.ForEach(w => this.UserList.Add(new User(w.Guid, w.TrusteeType, w.Username, w.Email, w.LastLogin, w.PasswordChanged, w.IsUsingTempPassword)));
};
client.GetUsersAsync();
}
}
I intend to use this class as I would use class derived from DbContext (if I could use Entity Framework which I cant). So far I have only users in the class.
I am using tis class in UsersController like this:
public class UsersController : Controller
{
private AdminDMSEntities adminEntities = new AdminDMSEntities();
//
// GET: /User/
public ActionResult Index()
{
return View(adminEntities.UserList);
}
}
The problem is that I will end up with InvalidOperationException, because controller is not waiting for async call completion and passes UserList to the view before it is properly filled with users.
I can have the call synchronous for the time being, but it is very likely I will be forced to use asynchronous calls later, so I would like to know how to ensure, that controller will wait for async call to be completed before UserList is passed to view...
Thanks in advance
EDIT: I have tried the approach with AsyncController as listed below, currently I have added this to AdminDMS entities class:
public static async Task<AdminDMSEntities> AdminDMSEntitiesAsync()
{
AdminDMSEntities result = null;
Task<AdminDMSEntities> getUsersAsyncTask = Task.Factory.StartNew(() => new AdminDMSEntities());
await getUsersAsyncTask;
return result;
}
and this is the change to the controller:
public class UsersController : AsyncController
{
private AdminDMSEntities adminEntities = null;
//
// GET: /User/
public async Task<ActionResult> Index()
{
if (adminEntities == null)
{
adminEntities = await AdminDMSEntities.AdminDMSEntitiesAsync();
}
return View(adminEntities.UserList);
}
}
The result is that adminEntities are containing an instance of the class, but there are no users in the list (there should be 11).
EDIT2: Since i was told that creating new task is not the right thing to do, I went with the first suggested approach removin AdminDMSEntities class from the code. My thanks to Darin for helping me out :)
You could use an asynchronous controller. The idea is to have your controller derive from the AsyncController class instead of the Controller class. This class provides methods that allow you to perform asynchronous operations.
For example:
public class MyController: AsyncController
{
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment();
var client = new SomeClient();
client.GetUsersCompleted += (s, e) =>
{
UserList = new List<User>();
AsyncManager.Parameters["users"] = e.Result.Select(
w => new User(
w.Guid,
w.TrusteeType,
w.Username,
w.Email,
w.LastLogin,
w.PasswordChanged,
w.IsUsingTempPassword
)
)
.ToList();
AsyncManager.OutstandingOperations.Decrement();
};
client.GetUsersAsync();
}
public ActionResult IndexCompleted(IEnumerable<User> users)
{
return View(users);
}
}
and if you are using .NET 4.5 you could even take advantage of the new async keyword simplifying the asynchronous code even further. This is possible if you refactor your data access layer to the new pattern (i.e. return Tasks):
public class MyController: AsyncController
{
public async Task<ActionResult> Index()
{
var client = new SomeClient();
var users = await client.GetUsersAsync().Select(
w => new User(
w.Guid,
w.TrusteeType,
w.Username,
w.Email,
w.LastLogin,
w.PasswordChanged,
w.IsUsingTempPassword
)
)
.ToList();
return View(users);
}
}

Resources