From 55159610a7f1a2540c0391570109c1723da1639f Mon Sep 17 00:00:00 2001 From: Ion Dormenco Date: Tue, 1 Nov 2022 17:25:25 +0200 Subject: [PATCH] Update README doc samples to latest (#188) * Update README doc samples to latest closes #172 . Also fixed navigation. Added links to explain OCP and SRP * Update README.md closes #171 * Update README.md --- README.md | 24 +++++++++++++++--------- docs/index.md | 2 +- sample/SampleEndpointApp/README.md | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index fe4d306..fdeafed 100644 --- a/README.md +++ b/README.md @@ -60,13 +60,13 @@ For version 3.0 we implemented a new way to define the base classes using "fluen [7. Related Articles](#7-related-articles) -[8. Videos and Podcasts](#8-videos-podcasts) +[8. Videos and Podcasts](#8-videos-and-podcasts) -[9. Related / Similar Projects](#8-related--similar-projects) +[9. Related / Similar Projects](#9-related--similar-projects) -[10. Projects Using ApiEndpoints](#9-projects-using-apiendpoints) +[10. Projects Using ApiEndpoints](#10-projects-using-apiendpoints) -[11. Success Stories and Testimonials](#10-success-stories-and-testimonials) +[11. Success Stories and Testimonials](#11-success-stories-and-testimonials) ## 1. Motivation @@ -97,7 +97,7 @@ Most REST APIs have groups of endpoints for a given resource. In Controller-base I'll look to add detailed documentation in the future but for now here's all you need to get started (you can also check the sample project): 1. Add the [Ardalis.ApiEndpoints NuGet package](https://www.nuget.org/packages/Ardalis.ApiEndpoints/) to your ASP.NET Core web project. -2. Create Endpoint classes by inheriting from either `BaseEndpoint` (for endpoints that accept a model as input) or `BaseEndpoint` (for endpoints that simply return a response). For example, a POST endpoint that creates a resource and then returns the newly created record would use the version that includes both a Request and a Response. A GET endpoint that just returns a list of records and doesn't accept any arguments would use the second version. +2. Create Endpoint classes by inheriting from either `EndpointBaseSync` (for endpoints that accept a model as input) or `EndpointBaseSync` (for endpoints that simply return a response). For example, a POST endpoint that creates a resource and then returns the newly created record would use the version that includes both a Request and a Response. A GET endpoint that just returns a list of records and doesn't accept any arguments would use the second version. 3. Implement the base class's abstract `Handle()` method. 4. Make sure to add a `[HttpGet]` or similar attribute to your `Handle()` method, specifying its route. 5. Define your `TResponse` type in a file in the same folder as its corresponding endpoint (or in the same file if you prefer). @@ -197,7 +197,7 @@ Below are what I expect will be some common questions: ### How do I use shared routing conventions? -~~If you want to create a common route template for all or some subset of your Endpoints, simply create a BaseEndpoint of your own that inherits from `Ardalis.Api.Endpoints.BaseEndpoint` and add a `[Route]` attribute to it.~~ +If you want to create a common route template for all or some subset of your Endpoints, simply create a EndpointBaseSync of your own that inherits from `Ardalis.Api.Endpoints.EndpointBaseSync` and add a `[Route]` attribute to it. After refactoring to use the fluent generics pattern, there is no longer a way to use a base class for a default route. Instead, you should define your routes as constants which you can store in a central file or in each Request DTO (the sample shows this approach). @@ -255,9 +255,15 @@ For more information, take a look at [this discussion](https://github.com/ardali There's an example in the [sample app](https://github.com/ardalis/ApiEndpoints/blob/main/sample/SampleEndpointApp/Endpoints/Authors/ListJsonFile.cs) that shows how to set this up and return a File actionresult. For the base type, just use the `WithoutResponse` option and in the endpoint handler return `File()`. ### How can I use model binding to pull values from multiple places, like `[FromRoute]`, `[FromBody]`, etc.? - +#172 The base endpoints only expose a single model type which is used on the Handle method, so you can't easily add additional parameters to the Handle method. However, you can put as many properties on the associated Request DTO as you want, and model binding allows you to set the same attributes per property as you would have set per parameter on the action method. See [Model Binding Docs](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding) and [discussion here in issue 42](https://github.com/ardalis/ApiEndpoints/issues/42) +### How can I use streaming from server to client? +There's an example in the [sample app](https://github.com/ardalis/ApiEndpoints/blob/main/sample/SampleEndpointApp/Endpoints/Authors/Stream.cs) that shows how to set this up and return an `IAsyncEnumerable`. For the base type, just use the `WithAsyncEnumerableResult` option and in the endpoint handler yeld return after awaiting your async code. + +> Note: streaming with IAsyncEnumerable does not work within Swagger Ui. Use curl to test this functionality +> ``` bash +> curl -X "GET" "https://localhost:44338/api/Authors/stream" -H "accept: text/plain" ## 6. Roadmap The following are some things I'd like to add to the project/package. @@ -268,7 +274,7 @@ Visual Studio and/or CLI item templates would make it much easier to create Endp ### Route Conventions -One thing that Controllers do have is built-in support in the framework to use their name in routes (e.g. "/[controller]/{id?}"). Currently in the sample app routes are hard-coded strings. It would be nice if there were an easy way to use a convention based on foldername or namespace or something (using foldername would align with how Razor Pages routing works). +One thing that Controllers do have is built-in support in the framework to use their name in routes (e.g. "`[controller]/{id?}`"). Currently in the sample app routes are hard-coded strings. It would be nice if there were an easy way to use a convention based on foldername or namespace or something (using foldername would align with how Razor Pages routing works). ## 7. Related Articles @@ -276,7 +282,7 @@ One thing that Controllers do have is built-in support in the framework to use t - [Decoupling Controllers with ApiEndpoints](https://betweentwobrackets.dev/posts/2020/09/decoupling-controllers-with-apiendpoints/) - [Fluent Generics](https://tyrrrz.me/blog/fluent-generics) -## 8 Videos and Podcasts +## 8. Videos and Podcasts - [Clean up your .NET Controllers with API Endpoints by Nick Chapsas](https://www.youtube.com/watch?v=SDu0MA6TmuM&ab_channel=NickChapsas) - [The .NET Docs Show - Controllers are Dinosaurs and the Case for API Endpoints](https://www.youtube.com/watch?v=9oroj2TmxBs&ab_channel=dotNET) diff --git a/docs/index.md b/docs/index.md index 8faef86..a02dea9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -14,7 +14,7 @@ When working with ASP.NET Core API Endpoints your project won't need any Control Most REST APIs have groups of endpoints for a given resource. In Controller-based projects you would have a controller per resource. When using API Endpoints you can simply create a folder per resource, just as you would use folders to group related pages in Razor Pages. -Instead of Model-View-Controller (MVC) the pattern becomes Request-EndPoint-Response(REPR). The REPR (reaper) pattern is much simpler and groups everything that has to do with a particular API endpoint together. It follows SOLID principles, in particular SRP and OCP. It also has all the benefits of feature folders and better follows the Common Closure Principle by grouping together things that change together. +Instead of Model-View-Controller (MVC) the pattern becomes Request-EndPoint-Response(REPR). The REPR (reaper) pattern is much simpler and groups everything that has to do with a particular API endpoint together. It follows SOLID principles, in particular [SRP](https://en.wikipedia.org/wiki/Single-responsibility_principle) and [OCP](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle). It also has all the benefits of feature folders and better follows the Common Closure Principle by grouping together things that change together. ## Installing Ardalis.ApiEndpoints diff --git a/sample/SampleEndpointApp/README.md b/sample/SampleEndpointApp/README.md index b19372c..addd81e 100644 --- a/sample/SampleEndpointApp/README.md +++ b/sample/SampleEndpointApp/README.md @@ -24,4 +24,4 @@ dotnet ef database update ## Duplicate Code in Endpoints -If the duplicate dependency code in the endpoints bothers you, you can avoid it easily by creating your own `BaseAppEndpoint` classes that inherit from `BaseEndpoint`. These can expose as properties any common dependencies like loggers, mappers, or generic repositories, and then use an IOC container's property injection feature to ensure they're always populated when the endpoint is created. \ No newline at end of file +If the duplicate dependency code in the endpoints bothers you, you can avoid it easily by creating your own `AppEndpointBaseSync` classes that inherit from `EndpointBaseSync`. These can expose as properties any common dependencies like loggers, mappers, or generic repositories, and then use an IOC container's property injection feature to ensure they're always populated when the endpoint is created. \ No newline at end of file