Skip to content

Commit

Permalink
Inherit from common base with attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
dasiths committed Mar 29, 2020
1 parent f6d60ac commit efcdfb2
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 52 deletions.
27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,27 +71,18 @@ The Endpoints are automatically inherited from a [`ControllerBase`](https://docs
If you really need an endpoint with a custom route, a mix of parameters coming from the Route/Query/Body or need full control over of any aspect then you can do something like this. Each of these class/method attributes works independently of each other and you can pick and choose them as required.

```c#
[Route("api/some-path/[endpoint]")] // results in "api/some-path/mycustom"
public class MyCustomEndpoint : AsyncEndpoint<Request, Result> // Use the http verb agnostic AsyncEndpoint class
[SimpleEndpoint(HttpVerb.Get)] // define the HTTP method being used
[Route("custom/[endpoint]")] // use your own route template (i.e this results in "custom/mysimple")
public class MySimpleEndpoint : SimpleEndpointBase // Extend the SimpleEndpointBase class
{
[NonAction] // mark this as non action to ignore this method when routing
public override async Task<ActionResult<Result>> HandleAsync(Request request, CancellationToken cancellationToken = default)
{
// actual logic here
}

// definew your custom signature binding attributes
[HttpGet("custom_path")]
public async Task<ActionResult<Result>> CustomMethod([FromQuery]string id, [FromBody]BodyModel model, CancellationToken cancellationToken)
[Route("custom-method")]
public ActionResult<GreetingResponse> MyMethodName([FromQuery] int id, [FromBody] GreetingRequest requestModel) // Mix of binding attributes
{
// map to the view model
var requestModel = new Request() {
id_property = id,
model_property = model
}

// pass to the handler method
return await this.HandleAsync(requestModel, cancellationToken);
return new GreetingResponse()
{
Greeting = $"Hello {requestModel.name} with id {id}"
};
}
}
```
Expand Down
32 changes: 8 additions & 24 deletions src/SimpleEndPoints/Core/Endpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,42 @@

namespace SimpleEndpoints.Core
{
[ApiController]
[Route("[endpoint]")]
public abstract class AsyncEndpoint : ControllerBase
public abstract class AsyncEndpoint : SimpleEndpointBase
{
public abstract Task<IActionResult> HandleAsync(CancellationToken cancellationToken = default);
}

[ApiController]
[Route("[endpoint]")]
public abstract class AsyncEndpointWithRequest<TRequest> : ControllerBase
public abstract class AsyncEndpointWithRequest<TRequest> : SimpleEndpointBase
{
public abstract Task<IActionResult> HandleAsync(TRequest requestModel, CancellationToken cancellationToken = default);
}

[ApiController]
[Route("[endpoint]")]
public abstract class AsyncEndpoint<TResponse> : ControllerBase
public abstract class AsyncEndpoint<TResponse> : SimpleEndpointBase
{
public abstract Task<ActionResult<TResponse>> HandleAsync(CancellationToken cancellationToken = default);
}

[ApiController]
[Route("[endpoint]")]
public abstract class AsyncEndpoint<TRequest, TResponse> : ControllerBase
public abstract class AsyncEndpoint<TRequest, TResponse> : SimpleEndpointBase
{
public abstract Task<ActionResult<TResponse>> HandleAsync(TRequest requestModel, CancellationToken cancellationToken = default);
}

[ApiController]
[Route("[endpoint]")]
public abstract class Endpoint : ControllerBase
public abstract class Endpoint : SimpleEndpointBase
{
public abstract IActionResult Handle();
}

[ApiController]
[Route("[endpoint]")]
public abstract class EndpointWithRequest<TRequest> : ControllerBase
public abstract class EndpointWithRequest<TRequest> : SimpleEndpointBase
{
public abstract IActionResult Handle(TRequest requestModel);
}

[ApiController]
[Route("[endpoint]")]
public abstract class Endpoint<TResponse> : ControllerBase
public abstract class Endpoint<TResponse> : SimpleEndpointBase
{
public abstract ActionResult<TResponse> Handle();
}

[ApiController]
[Route("[endpoint]")]
public abstract class Endpoint<TRequest, TResponse> : ControllerBase
public abstract class Endpoint<TRequest, TResponse> : SimpleEndpointBase
{
public abstract ActionResult<TResponse> Handle(TRequest requestModel);
}
Expand Down
10 changes: 10 additions & 0 deletions src/SimpleEndPoints/Core/SimpleEndpointBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Mvc;

namespace SimpleEndpoints.Core
{
[ApiController]
[Route("[endpoint]")]
public abstract class SimpleEndpointBase : ControllerBase
{
}
}
2 changes: 1 addition & 1 deletion src/SimpleEndPoints/SimpleEndpoints.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/dasiths/SimpleEndpoints</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>aspnetcore api controller endpoints</PackageTags>
<Version>1.4.2</Version>
<Version>1.4.3</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
30 changes: 21 additions & 9 deletions src/SimpleEndpoints.Example/Endpoints/Greeting/GreetingEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,41 @@ public class GreetingRequest
{
public string Name { get; set; }
}

public class GreetingResponse
{
public string Greeting { get; set; }
}

//As we are extending AsyncEndpoint we must specify the [HttpGet] attribute
public class GreetingAsyncEndpoint : AsyncEndpoint<GreetingRequest, GreetingResponse>
{
[HttpGet]
public override async Task<ActionResult<GreetingResponse>> HandleAsync([FromQuery] GreetingRequest requestModel,
CancellationToken cancellationToken = default)
{
return Ok(new GreetingResponse {Greeting = await Task.FromResult($"Hello {requestModel.Name}")});
return Ok(new GreetingResponse { Greeting = await Task.FromResult($"Hello {requestModel.Name}") });
}
}

//As we now extend AsyncGetEndpoint we know that the action is a Get so it can be omitted
public class GreetingAsyncGetEndpoint : AsyncGetEndpoint<GreetingRequest, GreetingResponse>
{
public override async Task<ActionResult<GreetingResponse>> HandleAsync([FromQuery] GreetingRequest requestModel,
CancellationToken cancellationToken = default)
{
return Ok(new GreetingResponse {Greeting = await Task.FromResult($"Hello {requestModel.Name}")});
return Ok(new GreetingResponse { Greeting = await Task.FromResult($"Hello {requestModel.Name}") });
}
}

//We can also overwrite the route segement - /api/GreetingAsyncGetWithRoute/get
public class GreetingAsyncGetWithRouteEndpoint : AsyncGetEndpoint<GreetingRequest, GreetingResponse>
{
[Route("get")]
public override async Task<ActionResult<GreetingResponse>> HandleAsync([FromQuery] GreetingRequest requestModel,
CancellationToken cancellationToken = default)
{
return Ok(new GreetingResponse {Greeting = await Task.FromResult($"Hello {requestModel.Name}")});
return Ok(new GreetingResponse { Greeting = await Task.FromResult($"Hello {requestModel.Name}") });
}
}

Expand All @@ -56,7 +56,7 @@ public class GreetingAsyncGetWithRouteEndpoint2 : AsyncGetEndpoint<GreetingReque
public override async Task<ActionResult<GreetingResponse>> HandleAsync([FromQuery] GreetingRequest requestModel,
CancellationToken cancellationToken = default)
{
return Ok(new GreetingResponse {Greeting = await Task.FromResult($"Hello {requestModel.Name}")});
return Ok(new GreetingResponse { Greeting = await Task.FromResult($"Hello {requestModel.Name}") });
}
}

Expand All @@ -68,7 +68,19 @@ public class GreetingAsyncPostWithContradictoryRouteAndHttpMethodEndpoint : Asyn
public override async Task<ActionResult<GreetingResponse>> HandleAsync([FromQuery] GreetingRequest requestModel,
CancellationToken cancellationToken = default)
{
return Ok(new GreetingResponse {Greeting = await Task.FromResult($"Hello {requestModel.Name}")});
return Ok(new GreetingResponse { Greeting = await Task.FromResult($"Hello {requestModel.Name}") });
}
}

//Here we are using an endpoint constructed from extending the base class
[SimpleEndpoint(HttpVerb.Get)]
[Route("custom/[endpoint]")]
public class GreetingAsyncGetUsingCustomEndpoint : SimpleEndpointBase
{
[Route("custom-method")]
public async Task<ActionResult<GreetingResponse>> CustomHandleMethod([FromQuery] int id, [FromQuery] string name)
{
return Ok(new GreetingResponse { Greeting = await Task.FromResult($"Hello {name} with id {id}") });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,20 @@ public async Task GreetingAsyncPostWithContradictoryRouteAndHttpMethodEndpoint()
responseContent.Greeting.ShouldBe("Hello peterparker");
response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
}

[Fact]
public async Task GreetingAsyncGetUsingCustomEndpoint()
{
// Arrange
using var client = _factory.CreateClient();

// Act
var response = await client.GetAsync($"{routePrefix}custom/GreetingAsyncGetUsingCustom/custom-method?id=1&name=peterparker");
var responseContent = JsonConvert.DeserializeObject<GreetingResponse>(await response.Content.ReadAsStringAsync());

// Assert
responseContent.Greeting.ShouldBe("Hello peterparker with id 1");
response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
}
}
}

0 comments on commit efcdfb2

Please sign in to comment.