Giter Club home page Giter Club logo

efcore.genericservices.aspnetcore's Introduction

EfCore.GenericServices.AspNetCore

This library provides converters from EfCore.GenericService and EfCore.GenericBizRunner status results to into two ASP.NET Core formats.

  1. ASP.NET Core MVC or Razor pages - copy to ModelState.
  2. ASP.NET Core Web API - form correct HTTP response.

The library also contains code for unit/integration testing of code that uses EfCore.GenericService or EfCore.GenericBizRunner in ASP.NET Core controllers. See information at end of the Readme file.

*NOTE: If you are interested in using EfCore.GenericService or EfCore.GenericBizRunner in Web APIs then see the article "How to write good, testable ASP.NET Core Web API code quickly" for more information.

MIT licence - available on NuGet.

1. ASP.NET Core MVC or Razor pages - copy status to ModelState

The extension method CopyErrorsToModelState which copy IStatusGeneric errors into ASP.NET Core's ModelState. Useful when working html/razor pages.

There are two forms of the method CopyErrorsToModelState - they are:

1a. CopyErrorsToModelState taking a dto

In ASP.NET Core MVC or Razor pages you need to return errors vai the ModelState, which has the property name and the error for that property. However it is important to NOT have a named property that doesn't appear in the class shown on the display, otherwise the error doesn't appear.

Because the Generic libraries can return errors with for properties not found in the display class there is a version of CopyErrorsToModelState that takes a parameter called displayDto and it will ensure the name is only set on properties that the displayDto has in it.

service.Status.CopyErrorsToModelState(ModelState, inputDto);

NOTE: If you are using CopyErrorsToModelState with razor pages then you have to provide a prefix to each property name that matches the name of the bound property in the Razor Page C# file. See this section for an explanation on why. Here is an example:

service.Status.CopyErrorsToModelState(ModelState, inputDto, nameof(Data));

1b. CopyErrorsToModelState without a dto

If there is no DTO/ViewModel involved in the service then there is a version that doesn't have a displayDto parameter. This leaves the property names left as they were when the error was reported.

service.Status.CopyErrorsToModelState(ModelState);

2. ASP.NET Core Web API - forming the correct HTTP response

If you are using ASP.NET Core Web API you need the status codes of the Generic Libraries turned into the correct HTTP response. The CreateResponse static class contains a series of extension methods to do this for you.

NOTE: See this example Web API controller for examples of using the CreateResponse extension methods.

There are the following versions for both the GenericService and GenericBizRunner libraries:

  • IActionResult Response(this IStatusGeneric status) - this returns the status without any results.
  • IActionResult ResponseWithValidCode(this IStatusGeneric status, int validStatusCode) - this returns the status without any results, using the validStatusCode if the status has no errors.
  • ActionResult<T> Response<T>(this IStatusGeneric status, T results) - this returns the status with the results as a json object.
  • ActionResult<T> ResponseWithValidCode<T>(this IStatusGeneric status, T results, int validStatusCode, int nullResultStatusCode = 204) - this returns the status with the results as a json object, using the validStatusCode if the status has no errors and the result isn't null, or if the result is null it returns the nullResultStatusCode, which defaults to 204.
  • ActionResult<T> Response(this IStatusGeneric status, ControllerBase controller, string routeName, object routeValues, T dto) - this can be used with a Create to return a 201 status and a url to obtain the created item

Return formats

1. Success, no results

The HTTP status code defaults to 200, but you can change this by using the ResponseWithValidCode version of the methods. The json sent looks like this:

{
  "message": "Successfully deleted a Todo Item"
}

2. Success, with results - result is not null

The HTTP status code defaults to 200, but you can change this by using the ResponseWithValidCode version of the methods. The json sent looks like this:

{
  "message": "Success",
  "results": {
    "id": 1,
    "name": "Create ASP.NET Core API project",
    "difficulty": 1
  }
}

3. Success, with results where the results is null

The HTTP status code is 204 (NoContent), but you can change this by using the ResponseWithValidCode version of the method and providing the third parameter, nullResultStatusCode with the new status code. The json sent looks like this:

{
  "message": "The Todo Item was not found."
}

4. Create, returning a url

The ActionResult<T> Response(this IStatusGeneric status, ControllerBase controller, string routeName, object routeValues, T dto) method uses Microsoft's CreatedAtRoute method to return an HTTP status of 201 (Created), with the url to obtain the created item. This matches the HTTP status code definitions for a 201 create. Here is an example response:

Response body

{
  "name": "string",
  "difficulty": 1
}

Response headers (see returned url on line 3)

 content-type: application/json; charset=utf-8 
 date: Thu, 30 Aug 2018 11:07:48 GMT 
 location: http://localhost:54074/api/ToDo/7 
 ... other parts removed 

4. Error

The HTTP status code is 400 (BadRequest). The json sent looks like this:

{
    "": [
        "Global error message"
    ],    
    
    "MyPropery": [
        "The property is required",
        "Another error on the same property"
    ]
}

NOTE: This error format is the one that ASP.NET Core WebAPI when it is set up to validate data on input.

Unit test/Integration test of Web APIs

I have added some extension methods in the class ResponseDecoders that:

  1. Provides you with the HTTP Status Code in the response.
  2. Converts Web API responses that were created by the CreateResponse extension method into a GenericServices.IStatusGeneric type. This allows you to inspect the Status, message, Errors and returned Result.

I have added a section to my article on Web API that talks about this feature in more detail. Also do look at IntegrationTestToDoController for an example of how the ResponseDecoders extension methods are used.

efcore.genericservices.aspnetcore's People

Contributors

jonpsmith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

efcore.genericservices.aspnetcore's Issues

Doesn't work with GenericServices 3.1.0

I updated the GenericServices and GenericBizRunner packages to 3.1.0 and this library doesn't work anymore. I get the following error:

Reference to type 'IStatusGeneric' claims it is defined in 'GenericServices', but it could not be found

Asp.Net Core 2.2 Reference causing Mvc Analyzer errors

Hi Jon, thank you for your work with these libraries.
I was wondering if there is a reason you reference ASP.Net Core 2.2 and not > 2.2? With 2.2 references in a 3.1 project I am getting Analyzer errors (due to the Microsoft.AspNetCore.Mvc.Analyzers being referenced. Does it have to do with framework references? To get around this with a 3.1 project, is it best to simply duplicate your code in our project?

Perhaps I am missing something, I would very happy to hear your thoughts on this.

Error is like so:
>CSC : warning AD0001: Analyzer 'Microsoft.AspNetCore.Mvc.Analyzers.TopLevelParameterNameAnalyzer' threw an exception of type 'System.NullReferenceException' with message 'Object reference not set to an instance of an object.'.

Accessing DTO mappers

This is question about EfCore.GenericServices and EfCore.GenericBizRunner but in the context of ASP NET Core app so Im asking here.

Let's say I have a Post endpoint which takes in a CreateTodoDto model, similar to the example, but instead of returning a CreatedAt response with a TodoItemresult, I want to return a TodoItemDto model as an abstraction over the database entity.

I changed the ICreateTodoBizLogic to return a TodoItemDto, however I find myself having to manually build the DTO to be returned. How can I take advantage of the fact that I have used ILinkToEntity<TodoItem> and access the mapper that has already been setup?

In a similar vein, how can I take advantage of CreateTodoDto being linked to TodoItem to automatically populate properties of TodoItem rather than building from scratch inside the BizAction method?

Hope this makes sense, thanks in advance for your response.

Converting IStatusGeneric to ProblemDetails

Is there any work in progress to support converting IStatusGeneric to ProblemDetails? Or an alternate package that does? It doesn't really make sense to use the ASP.NET 2.1 format for new APIs.

Writing to the response body is invalid for responses with status code 204.

Using in .NET Core 3.1

EfCore.GenericBizRunner 4.0.0
EfCore.GenericServices.AspNetCore 4.0.0

The following code

        [HttpGet("{id}", Name = "GetSingleRelation")]
        public async Task<ActionResult<WebApiMessageAndResult<RelationEntity>>> GetSingleAsync(Guid id, [FromServices]ICrudServicesAsync<DataDbContext> service)
        {
            var relation = await service.ReadSingleAsync<RelationEntity>(id);
            return service.Response(relation);
        }

service.Response(null) says

This will return a result value, with the status Message 1. If there are no errors and the result is not null it will return a HTTP 200 response plus a json containing the message from the status and the results object 2. If there are no errors but result is null it will return a HTTP 204 (NoContent) with the status Message 3. If there are errors it returns a HTTP 400 with the error information in the standard WebAPI format

However null results into the following exception:

System.InvalidOperationException: Writing to the response body is invalid for responses with status code 204.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowWritingToResponseBodyNotSupported()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FirstWriteAsyncInternal(ReadOnlyMemory1 data, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FirstWriteAsync(ReadOnlyMemory1 data, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.WritePipeAsync(ReadOnlyMemory1 data, CancellationToken cancellationToken) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.WriteAsync(ReadOnlyMemory1 source, CancellationToken cancellationToken)
at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|21_0(ResourceInvoker invoker, IActionResult result)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

I have removed all formatters and just using a clean "UseMVC()" in my startup.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.