Skip to content

Commit 50f1e11

Browse files
author
Jennifer Deigendesch
committed
Added attributes and filters
1 parent 1552480 commit 50f1e11

File tree

10 files changed

+364
-0
lines changed

10 files changed

+364
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace AspNetCoreExample.Attributes
2+
{
3+
using System;
4+
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.AspNetCore.Mvc.Filters;
7+
8+
[AttributeUsage(AttributeTargets.Method)]
9+
public class ValidateModelAttribute : ActionFilterAttribute
10+
{
11+
public override void OnActionExecuting(ActionExecutingContext context)
12+
{
13+
if (context.ActionArguments.Values.Contains(null))
14+
{
15+
context.Result = new BadRequestResult();
16+
}
17+
18+
if (!context.ModelState.IsValid)
19+
{
20+
context.Result = new BadRequestObjectResult(context.ModelState);
21+
}
22+
}
23+
}
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace AspNetCoreExample.Attributes
2+
{
3+
using System;
4+
5+
using AspNetCoreExample.Filters;
6+
7+
using Microsoft.AspNetCore.Mvc;
8+
9+
/// <summary>
10+
/// Attribute that validates if the user exists for the given id.
11+
/// </summary>
12+
[AttributeUsage(AttributeTargets.Method)]
13+
public class ValidateUserExistsAttribute : TypeFilterAttribute
14+
{
15+
public ValidateUserExistsAttribute()
16+
: base(typeof(ValidateUserExistsFilter))
17+
{
18+
}
19+
}
20+
}

ASPNETCoreExample/Controllers/UserController.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Threading.Tasks;
55

6+
using AspNetCoreExample.Attributes;
67
using AspNetCoreExample.Repositories;
78

89
using DataDAL.Tables;
@@ -33,6 +34,7 @@ public UserController(IUserRepository repository, ILogger<UserController> logger
3334
[HttpGet("{id}")]
3435
[ProducesResponseType(typeof(User), 200)]
3536
[ProducesResponseType(typeof(string), 400)]
37+
[ValidateUserExists]
3638
public async Task<IActionResult> GetUserAsync(string id)
3739
{
3840
try
@@ -47,5 +49,28 @@ public async Task<IActionResult> GetUserAsync(string id)
4749
return this.BadRequest(errorMessage);
4850
}
4951
}
52+
53+
/// <summary>
54+
/// Add the given user.
55+
/// </summary>
56+
/// <param name="user">User to add.</param>
57+
/// <response code="400">If adding the user failed.</response>
58+
[HttpPost]
59+
[ProducesResponseType(typeof(string), 400)]
60+
[ValidateModel]
61+
public async Task<IActionResult> SetUserAsync([FromBody] User user)
62+
{
63+
try
64+
{
65+
await this.repository.SetUserAsync(user);
66+
return this.Ok();
67+
}
68+
catch (Exception e)
69+
{
70+
string errorMessage = $"Error while adding the user: {e.InnerException?.Message ?? e.Message}";
71+
this.logger.LogError(errorMessage);
72+
return this.BadRequest(errorMessage);
73+
}
74+
}
5075
}
5176
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
namespace AspNetCoreExample.Filters
2+
{
3+
using System.Threading.Tasks;
4+
5+
using DataDAL;
6+
7+
using Microsoft.AspNetCore.Mvc;
8+
using Microsoft.AspNetCore.Mvc.Filters;
9+
using Microsoft.EntityFrameworkCore;
10+
11+
public class ValidateUserExistsFilter : IAsyncActionFilter
12+
{
13+
private const string UserIdKey = "id";
14+
15+
private readonly DataDAL dataDal;
16+
17+
public ValidateUserExistsFilter(DataDAL dataDal)
18+
{
19+
this.dataDal = dataDal;
20+
}
21+
22+
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
23+
{
24+
if (!context.ActionArguments.ContainsKey(UserIdKey))
25+
{
26+
context.Result = new BadRequestResult();
27+
return;
28+
}
29+
30+
if (!await this.UserExists(context))
31+
{
32+
context.Result = new ForbidResult();
33+
return;
34+
}
35+
36+
await next();
37+
}
38+
39+
private async Task<bool> UserExists(ActionExecutingContext context)
40+
{
41+
string userId = context.ActionArguments[UserIdKey]
42+
.ToString();
43+
return await this.dataDal.User.CountAsync(item => item.Id == userId) > 0;
44+
}
45+
}
46+
}

ASPNETCoreExample/Repositories/IUserRepository.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@
77
public interface IUserRepository
88
{
99
Task<User> GetUserAsync(string id);
10+
11+
Task SetUserAsync(User user);
1012
}
1113
}

ASPNETCoreExample/Repositories/UserRepository.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,11 @@ public async Task<User> GetUserAsync(string id)
2222
return await this.dataDal.User.Where(item => item.Id == id)
2323
.FirstAsync();
2424
}
25+
26+
public async Task SetUserAsync(User user)
27+
{
28+
var newUser = await this.dataDal.User.AddAsync(user);
29+
await newUser.Context.SaveChangesAsync();
30+
}
2531
}
2632
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace Tests.AttributeTests
2+
{
3+
using System.Collections.Generic;
4+
5+
using AspNetCoreExample.Controllers;
6+
7+
using Microsoft.AspNetCore.Http;
8+
using Microsoft.AspNetCore.Mvc;
9+
using Microsoft.AspNetCore.Mvc.Abstractions;
10+
using Microsoft.AspNetCore.Mvc.Filters;
11+
using Microsoft.AspNetCore.Routing;
12+
using Microsoft.VisualStudio.TestTools.UnitTesting;
13+
14+
using Moq;
15+
16+
public class AttributeTestBase
17+
{
18+
protected UserController Controller { get; private set; }
19+
20+
[TestInitialize]
21+
public virtual void SetUp()
22+
{
23+
this.Controller = new UserController(null, null);
24+
}
25+
26+
protected ActionExecutingContext CreateActionExecutingContext(object controller, IDictionary<string, object> requestParameters)
27+
{
28+
var httpContextMock = new Mock<HttpContext>();
29+
var routeDataMock = new Mock<RouteData>();
30+
var actionDescriptorMock = new Mock<ActionDescriptor>();
31+
var actionContext = new ActionContext(httpContextMock.Object, routeDataMock.Object, actionDescriptorMock.Object);
32+
33+
var filters = new List<IFilterMetadata>();
34+
return new ActionExecutingContext(actionContext, filters, requestParameters, controller);
35+
}
36+
}
37+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace Tests.AttributeTests
2+
{
3+
using System.Collections.Generic;
4+
5+
using AspNetCoreExample.Attributes;
6+
7+
using DataDAL.Tables;
8+
9+
using Microsoft.AspNetCore.Mvc;
10+
using Microsoft.VisualStudio.TestTools.UnitTesting;
11+
12+
[TestClass]
13+
public class ValidateModelTests : AttributeTestBase
14+
{
15+
private const string ModelKey = "Model";
16+
17+
private ValidateModelAttribute attributeToTest;
18+
19+
[TestInitialize]
20+
public override void SetUp()
21+
{
22+
base.SetUp();
23+
this.attributeToTest = new ValidateModelAttribute();
24+
}
25+
26+
[TestMethod]
27+
public void ShouldReturnOkIfModelIsValid()
28+
{
29+
// Arrange
30+
var model = new User();
31+
var requestParameter = this.GetValidateModelAttributeRequestParameters(model);
32+
var actionExecutingContext = this.CreateActionExecutingContext(this.Controller, requestParameter);
33+
34+
// Test
35+
this.attributeToTest.OnActionExecuting(actionExecutingContext);
36+
37+
// Assert
38+
Assert.IsNull(actionExecutingContext.Result);
39+
}
40+
41+
[TestMethod]
42+
public void ShouldReturnBadRequestIfModelIsInvalid()
43+
{
44+
// Arrange
45+
const string InvalidModel = "test";
46+
var requestParameter = this.GetValidateModelAttributeRequestParameters(InvalidModel);
47+
var actionExecutingContext = this.CreateActionExecutingContext(this.Controller, requestParameter);
48+
actionExecutingContext.ModelState.AddModelError(ModelKey, "Invalid Model");
49+
50+
// Test
51+
this.attributeToTest.OnActionExecuting(actionExecutingContext);
52+
53+
// Assert
54+
Assert.IsNotNull(actionExecutingContext.Result);
55+
Assert.IsInstanceOfType(actionExecutingContext.Result, typeof(BadRequestObjectResult));
56+
}
57+
58+
[TestMethod]
59+
public void ShouldReturnBadRequestIfModelIsNotAvailable()
60+
{
61+
// Arrange
62+
const string NotAvailableModel = null;
63+
var requestParameter = this.GetValidateModelAttributeRequestParameters(NotAvailableModel);
64+
var actionExecutingContext = this.CreateActionExecutingContext(this.Controller, requestParameter);
65+
66+
// Test
67+
this.attributeToTest.OnActionExecuting(actionExecutingContext);
68+
69+
// Assert
70+
Assert.IsNotNull(actionExecutingContext.Result);
71+
Assert.IsInstanceOfType(actionExecutingContext.Result, typeof(BadRequestResult));
72+
}
73+
74+
private IDictionary<string, object> GetValidateModelAttributeRequestParameters(object model)
75+
{
76+
return new Dictionary<string, object> { { ModelKey, model } };
77+
}
78+
}
79+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
namespace Tests.FilterTests
2+
{
3+
using System.Collections.Generic;
4+
5+
using AspNetCoreExample.Controllers;
6+
7+
using Microsoft.AspNetCore.Http;
8+
using Microsoft.AspNetCore.Mvc;
9+
using Microsoft.AspNetCore.Mvc.Abstractions;
10+
using Microsoft.AspNetCore.Mvc.Filters;
11+
using Microsoft.AspNetCore.Routing;
12+
using Microsoft.VisualStudio.TestTools.UnitTesting;
13+
14+
using Moq;
15+
16+
using Tests.RepositoryTests;
17+
18+
public class FilterTestBase : TestBase
19+
{
20+
protected ActionExecutionDelegate DelegateMock { get; private set; }
21+
22+
protected UserController Controller { get; private set; }
23+
24+
[TestInitialize]
25+
public override void SetUp()
26+
{
27+
base.SetUp();
28+
29+
this.DelegateMock = new Mock<ActionExecutionDelegate>().Object;
30+
this.Controller = new UserController(null, null);
31+
}
32+
33+
protected ActionExecutingContext CreateActionExecutingContext(object controller, IDictionary<string, object> requestParameters)
34+
{
35+
var httpContextMock = new Mock<HttpContext>();
36+
var routeDataMock = new Mock<RouteData>();
37+
var actionDescriptorMock = new Mock<ActionDescriptor>();
38+
var actionContext = new ActionContext(httpContextMock.Object, routeDataMock.Object, actionDescriptorMock.Object);
39+
40+
var filters = new List<IFilterMetadata>();
41+
return new ActionExecutingContext(actionContext, filters, requestParameters, controller);
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)