Skip to content

Commit 91fcbb9

Browse files
authored
Merge pull request #123 from Research-Institute/feat/#122
filter actions using attributes
2 parents 4ddf6cb + 6b77e0d commit 91fcbb9

File tree

6 files changed

+453
-0
lines changed

6 files changed

+453
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection;
4+
using System.Threading.Tasks;
5+
using JsonApiDotNetCore.Controllers;
6+
using JsonApiDotNetCore.Internal;
7+
using Microsoft.AspNetCore.Mvc.Filters;
8+
9+
namespace JsonApiDotNetCore.Controllers
10+
{
11+
public abstract class HttpRestrictAttribute : ActionFilterAttribute, IAsyncActionFilter
12+
{
13+
protected abstract string[] Methods { get; }
14+
15+
public override async Task OnActionExecutionAsync(
16+
ActionExecutingContext context,
17+
ActionExecutionDelegate next)
18+
{
19+
var method = context.HttpContext.Request.Method;
20+
21+
if(CanExecuteAction(method) == false)
22+
throw new JsonApiException("405", $"This resource does not support {method} requests.");
23+
24+
await next();
25+
}
26+
27+
private bool CanExecuteAction(string requestMethod)
28+
{
29+
return Methods.Contains(requestMethod) == false;
30+
}
31+
}
32+
33+
public class HttpReadOnlyAttribute : HttpRestrictAttribute
34+
{
35+
protected override string[] Methods { get; } = new string[] { "POST", "PATCH", "DELETE" };
36+
}
37+
38+
public class NoHttpPostAttribute : HttpRestrictAttribute
39+
{
40+
protected override string[] Methods { get; } = new string[] { "POST" };
41+
}
42+
43+
public class NoHttpPatchAttribute : HttpRestrictAttribute
44+
{
45+
protected override string[] Methods { get; } = new string[] { "PATCH" };
46+
}
47+
48+
public class NoHttpDeleteAttribute : HttpRestrictAttribute
49+
{
50+
protected override string[] Methods { get; } = new string[] { "DELETE" };
51+
}
52+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using JsonApiDotNetCore.Controllers;
2+
using Microsoft.AspNetCore.Mvc;
3+
4+
namespace JsonApiDotNetCoreExample.Controllers.Restricted
5+
{
6+
[Route("[controller]")]
7+
[HttpReadOnly]
8+
public class ReadOnlyController : Controller
9+
{
10+
[HttpGet]
11+
public IActionResult Get() => Ok();
12+
13+
[HttpPost]
14+
public IActionResult Post() => Ok();
15+
16+
[HttpPatch]
17+
public IActionResult Patch() => Ok();
18+
19+
[HttpDelete]
20+
public IActionResult Delete() => Ok();
21+
}
22+
23+
[Route("[controller]")]
24+
[NoHttpPost]
25+
public class NoHttpPostController : Controller
26+
{
27+
[HttpGet]
28+
public IActionResult Get() => Ok();
29+
30+
[HttpPost]
31+
public IActionResult Post() => Ok();
32+
33+
[HttpPatch]
34+
public IActionResult Patch() => Ok();
35+
36+
[HttpDelete]
37+
public IActionResult Delete() => Ok();
38+
}
39+
40+
[Route("[controller]")]
41+
[NoHttpPatch]
42+
public class NoHttpPatchController : Controller
43+
{
44+
[HttpGet]
45+
public IActionResult Get() => Ok();
46+
47+
[HttpPost]
48+
public IActionResult Post() => Ok();
49+
50+
[HttpPatch]
51+
public IActionResult Patch() => Ok();
52+
53+
[HttpDelete]
54+
public IActionResult Delete() => Ok();
55+
}
56+
57+
[Route("[controller]")]
58+
[NoHttpDelete]
59+
public class NoHttpDeleteController : Controller
60+
{
61+
[HttpGet]
62+
public IActionResult Get() => Ok();
63+
64+
[HttpPost]
65+
public IActionResult Post() => Ok();
66+
67+
[HttpPatch]
68+
public IActionResult Patch() => Ok();
69+
70+
[HttpDelete]
71+
public IActionResult Delete() => Ok();
72+
}
73+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System.Net;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using JsonApiDotNetCoreExample;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.AspNetCore.TestHost;
7+
using Xunit;
8+
9+
namespace JsonApiDotNetCoreExampleTests.Acceptance
10+
{
11+
[Collection("WebHostCollection")]
12+
public class HttpReadOnlyTests
13+
{
14+
[Fact]
15+
public async Task Allows_GET_Requests()
16+
{
17+
// arrange
18+
const string route = "readonly";
19+
const string method = "GET";
20+
21+
// act
22+
var statusCode = await MakeRequestAsync(route, method);
23+
24+
// assert
25+
Assert.Equal(HttpStatusCode.OK, statusCode);
26+
}
27+
28+
[Fact]
29+
public async Task Rejects_POST_Requests()
30+
{
31+
// arrange
32+
const string route = "readonly";
33+
const string method = "POST";
34+
35+
// act
36+
var statusCode = await MakeRequestAsync(route, method);
37+
38+
// assert
39+
Assert.Equal(HttpStatusCode.MethodNotAllowed, statusCode);
40+
}
41+
42+
[Fact]
43+
public async Task Rejects_PATCH_Requests()
44+
{
45+
// arrange
46+
const string route = "readonly";
47+
const string method = "PATCH";
48+
49+
// act
50+
var statusCode = await MakeRequestAsync(route, method);
51+
52+
// assert
53+
Assert.Equal(HttpStatusCode.MethodNotAllowed, statusCode);
54+
}
55+
56+
[Fact]
57+
public async Task Rejects_DELETE_Requests()
58+
{
59+
// arrange
60+
const string route = "readonly";
61+
const string method = "DELETE";
62+
63+
// act
64+
var statusCode = await MakeRequestAsync(route, method);
65+
66+
// assert
67+
Assert.Equal(HttpStatusCode.MethodNotAllowed, statusCode);
68+
}
69+
70+
private async Task<HttpStatusCode> MakeRequestAsync(string route, string method)
71+
{
72+
var builder = new WebHostBuilder()
73+
.UseStartup<Startup>();
74+
var httpMethod = new HttpMethod(method);
75+
var server = new TestServer(builder);
76+
var client = server.CreateClient();
77+
var request = new HttpRequestMessage(httpMethod, route);
78+
var response = await client.SendAsync(request);
79+
return response.StatusCode;
80+
}
81+
}
82+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System.Net;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using JsonApiDotNetCoreExample;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.AspNetCore.TestHost;
7+
using Xunit;
8+
9+
namespace JsonApiDotNetCoreExampleTests.Acceptance
10+
{
11+
[Collection("WebHostCollection")]
12+
public class nohttpdeleteTests
13+
{
14+
[Fact]
15+
public async Task Allows_GET_Requests()
16+
{
17+
// arrange
18+
const string route = "nohttpdelete";
19+
const string method = "GET";
20+
21+
// act
22+
var statusCode = await MakeRequestAsync(route, method);
23+
24+
// assert
25+
Assert.Equal(HttpStatusCode.OK, statusCode);
26+
}
27+
28+
[Fact]
29+
public async Task Allows_POST_Requests()
30+
{
31+
// arrange
32+
const string route = "nohttpdelete";
33+
const string method = "POST";
34+
35+
// act
36+
var statusCode = await MakeRequestAsync(route, method);
37+
38+
// assert
39+
Assert.Equal(HttpStatusCode.OK, statusCode);
40+
}
41+
42+
[Fact]
43+
public async Task Allows_PATCH_Requests()
44+
{
45+
// arrange
46+
const string route = "nohttpdelete";
47+
const string method = "PATCH";
48+
49+
// act
50+
var statusCode = await MakeRequestAsync(route, method);
51+
52+
// assert
53+
Assert.Equal(HttpStatusCode.OK, statusCode);
54+
}
55+
56+
[Fact]
57+
public async Task Rejects_DELETE_Requests()
58+
{
59+
// arrange
60+
const string route = "nohttpdelete";
61+
const string method = "DELETE";
62+
63+
// act
64+
var statusCode = await MakeRequestAsync(route, method);
65+
66+
// assert
67+
Assert.Equal(HttpStatusCode.MethodNotAllowed, statusCode);
68+
}
69+
70+
private async Task<HttpStatusCode> MakeRequestAsync(string route, string method)
71+
{
72+
var builder = new WebHostBuilder()
73+
.UseStartup<Startup>();
74+
var httpMethod = new HttpMethod(method);
75+
var server = new TestServer(builder);
76+
var client = server.CreateClient();
77+
var request = new HttpRequestMessage(httpMethod, route);
78+
var response = await client.SendAsync(request);
79+
return response.StatusCode;
80+
}
81+
}
82+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System.Net;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using JsonApiDotNetCoreExample;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.AspNetCore.TestHost;
7+
using Xunit;
8+
9+
namespace JsonApiDotNetCoreExampleTests.Acceptance
10+
{
11+
[Collection("WebHostCollection")]
12+
public class nohttppatchTests
13+
{
14+
[Fact]
15+
public async Task Allows_GET_Requests()
16+
{
17+
// arrange
18+
const string route = "nohttppatch";
19+
const string method = "GET";
20+
21+
// act
22+
var statusCode = await MakeRequestAsync(route, method);
23+
24+
// assert
25+
Assert.Equal(HttpStatusCode.OK, statusCode);
26+
}
27+
28+
[Fact]
29+
public async Task Allows_POST_Requests()
30+
{
31+
// arrange
32+
const string route = "nohttppatch";
33+
const string method = "POST";
34+
35+
// act
36+
var statusCode = await MakeRequestAsync(route, method);
37+
38+
// assert
39+
Assert.Equal(HttpStatusCode.OK, statusCode);
40+
}
41+
42+
[Fact]
43+
public async Task Rejects_PATCH_Requests()
44+
{
45+
// arrange
46+
const string route = "nohttppatch";
47+
const string method = "PATCH";
48+
49+
// act
50+
var statusCode = await MakeRequestAsync(route, method);
51+
52+
// assert
53+
Assert.Equal(HttpStatusCode.MethodNotAllowed, statusCode);
54+
}
55+
56+
[Fact]
57+
public async Task Allows_DELETE_Requests()
58+
{
59+
// arrange
60+
const string route = "nohttppatch";
61+
const string method = "DELETE";
62+
63+
// act
64+
var statusCode = await MakeRequestAsync(route, method);
65+
66+
// assert
67+
Assert.Equal(HttpStatusCode.OK, statusCode);
68+
}
69+
70+
private async Task<HttpStatusCode> MakeRequestAsync(string route, string method)
71+
{
72+
var builder = new WebHostBuilder()
73+
.UseStartup<Startup>();
74+
var httpMethod = new HttpMethod(method);
75+
var server = new TestServer(builder);
76+
var client = server.CreateClient();
77+
var request = new HttpRequestMessage(httpMethod, route);
78+
var response = await client.SendAsync(request);
79+
return response.StatusCode;
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)