GithubHelp home page GithubHelp logo

cartercommunity / carter Goto Github PK

View Code? Open in Web Editor NEW
2.0K 50.0 171.0 23.7 MB

Carter is framework that is a thin layer of extension methods and functionality over ASP.NET Core allowing code to be more explicit and most importantly more enjoyable.

License: MIT License

C# 97.35% Shell 0.84% PowerShell 1.81%
nancy asp-net-core csharp asp-net mit-license dotnet-core middleware

carter's People

Contributors

azure-pipelines[bot] avatar bugagain avatar collinalpert avatar dreamwalker666 avatar gkinsman avatar grandchamp avatar henrikrxn avatar j0nnyhughes avatar jaxelr avatar jchannon avatar joestead avatar josephwoodward avatar jussimattila avatar khalidabuhakmeh avatar klym1 avatar martincostello avatar mderriey avatar niklashansen avatar onkelmato avatar pauliusnorkus avatar pdwetz avatar petedishman avatar poke avatar ragingkore avatar scardetto avatar sebastienros avatar simoncropp avatar sphiecoh avatar tomzo avatar williamhbell 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  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  avatar  avatar

Watchers

 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  avatar

carter's Issues

Register Validators as scoped instances rather than singleton

I had a setup a validation as below wherein RoleManager via constructor injection is a scoped instance and this resulted in an error Cannot consume scoped service in Singleton (validators are registered as Singleton within CarterExtensions.cs).

public class CommandValidator : AbstractValidator<Command>
{
    public CommandValidator(RoleManager<UserRole> roleManager)
    {
        RuleFor(c => c.Name).CustomAsync(async (name, context, cancel) =>
        {
            if (string.IsNullOrEmpty(name))
            {
                context.AddFailure("Role name is required");
            }
            else
            {
                var role = await roleManager.FindByNameAsync(name);

                if (role != null)
                {
                    context.AddFailure("Role name already exists");
                }
            }
        });
    }
}

It would be good to register validators as scoped instances.

@jchannon if you could confirm on this change, I would be happy to send my first PR to Carter :-)

Add XML docs to public methods

  • BindExtensions - done via #46
  • ValidationExtensions - done via #47
  • QueryStringExtensions - done via #47
  • RouteDataExtensions - done via #49
  • StreamExtensions - done via #48
  • ResponseExtensions - done via #49
  • BotwinExtensions - done via #50
  • BotwinModule - done via #55
  • BotwinOptions - done via #56
  • IValidatorLocator - done via #57
  • IStatusCodeHandler - done via #58
  • IResponseNegotiator - done via #49

Use C#7 tuples in BotwinModule

It currently uses Tuple.Create and therefore the extension class refers to the route handlers as Item3. We can tidy that up!

Base path on module

Currently have to do:

this.Get("/api/runs", this.GetUITestRuns);
this.Post("/api/runs", this.CreateTestRun);

If /api/ was a base path it would clean things up a bit

Binding from Form data can throw if there is a mismatch in fields.

When binding from a FormCollection you can get a null reference exception if the fields do not match up properly.

Given the type Document:

public class Document
{
   public string Type { get; set; }
}

and the form data:

name="type"[...]

you will get a null reference exception because of the erroneous quotes.

Instead of throwing, it should not bind that data - and should fail validation if it is present

BindAndSave file to add optional fileName argument

This is so the filename can be saved as a user requested filename rather than use the filename as sent in the request

https://github.com/jchannon/Botwin/blob/master/src/ModelBinding/BindExtensions.cs#L99

public static async Task BindAndSaveFile(this HttpRequest request, string saveLocation, string fileName = "")
        {
            var file = await request.BindFile();
            fileName = string.IsNullOrWhiteSpace(fileName) ? file.FileName : fileName;
            using (var fileToSave = File.Create(Path.Combine(saveLocation, fileName)))
            {
                await file.CopyToAsync(fileToSave);
            }
        }

Question about services lifecycles

I´m trying to use the Botwin library with EF Core. I registered the DbContext using services.AddDbContext (By default sets ServiceLifetime.Scoped) but I noticed that my application uses the same DbContext in each request. Then I noticed that the modules are created once, regardless of whether they are registered at IoC container as transient. So each dependency injected don't respect its configured Lifetime. Is this a desing decision?

Thanks in advance.

Accept RouteMetaData instance

Thanks for the OpenAPI work, it would be nice if the route could accept an instance of RouteMetaData as an alternative to a type name. I'm not too concerned about how that instance would get passed but I'd like to avoid the creation of new classes for each route just to document it. Kind of defeats the purpose of using Carter in the first place in my opinion.

Unable to respond with a Stream

I'm unable to work out how to reply with a streamed response using Botwin. Reading up on ASP.NET Core it looks liked I'd be able to do the following:

[HttpGet]
public FileStreamResult GetTest()
{
  var stream = new MemoryStream(Encoding.ASCII.GetBytes("Hello World"));
  return new FileStreamResult(stream, new MediaTypeHeaderValue("text/plain"))
  {
    FileDownloadName = "test.txt"
  };
}

(https://stackoverflow.com/questions/42771409/how-to-stream-with-asp-net-core#42772150)

However, I can't work out how I can do this using the HttpResponse I have from the BotwinModule handler?

Here's a snippet of my module, but it doesn't return any data:

var models = this.journalReader.Read(start, end, type, ct);

var fileStream =
    new FileStreamResult(CreateExportStream(models, requestLog, ct), "application/csv")
        {
            FileDownloadName
                = $"journal-{type}.csv"
        };

res.Body = fileStream.FileStream;

CarterModule.Get() automatically registers route for both GET and HEAD verbs

I noticed this when trying to debug why I couldn't register a different handler for HEAD when GET was already registered for the same route. It's not something that I can't work around, of course, but I'm still wondering: is there a good reason why CarterModule.Get() would want to implicitly cover HEAD as well?

Use the ILogger stuff to add debug level tracing

As we're using the IServiceProvider provided by AspNetCore, we should hook up some of the other features, including enabling logging.

It's a pain to try and diagnose issues with things without any logging, we should implement trace/debug calls around the code base that can be configured with the ILoggerFactory

Route Maps

When Botwin loads it has to construct all of the BotwinModules, which in turn has to construct all of its dependencies. I assume it does this so it can determine all the relevant routes so it knows which Module to construct when an associated request comes in.

This can be problematic if you have (like I often do) a lot of BotwinModules. Each module generally only defines one or two routes. This can slow down startup because of DI basically.

I've been thinking if it would make sense to have some sort of interface that Botwin could look for at startup/load to get routes and mappings instead of constructing all Modules.

https://gist.github.com/dcomartin/81f09f24aeea004b831d96b0d49c3d90

Thoughts?

Test dotnet new botwin

Run

dotnet new -i BotwinTemplate

dotnet new botwin -n MyBotwinApp

Currently seeing object reference error but others have reported all is ok.

Update sample/documentation regarding authentication

It would be useful to update the sample app and documentation, detailing how to use Botwin along with authentication middleware.

Although it's relatively easy to implement such functionality using the Before / After handlers (as in Nancy), it would be helpful to describe the equivalent of the Authorize attribute and perhaps add attributes or extension methods to ease the process.

Renaming to Carter

Thinking of renaming Botwin to Carter.

Consensus seems to be that Botwin, whilst I think is genius, and as a benevolent dictator you cannot disagree with me, is confusing some folks as they think it's a Bot framework for Windows so after too many hours thinking about it I've come up with Carter.

Carter comes from the surname of Jay-Z (Shawn Carter) and in his song Empire State of Mind he sings "I'm the new Sinatra". Sinatra is a web framework which inspired Nancy which heavily inspired Botwin.

Unless there is any real objection like people think this is a library for giving instructions on how to cart their mother in law away ("cart - her") we'll rename this in the coming few weeks.

Let me know what you think

Route clash with parametrized routes -> 405 Method not allowed

Consider the following:

public class ThingsModule : CarterModule
{
  public ThingsModule() : base("things")
  {
    Get("/", GetAllThings);
    Get("{type}", GetThing);
    Post("horse", PostHorse); //will not work. GET {type} will be selected, and return 405 Method not allowed
  }

  private Task GetAllThings(HttpRequest req, HttpResponse res, RouteData routeData)
  {
    return res.WriteAsync("Horse, anvil, cucumber");
  }

  private Task PostHorse(HttpRequest req, HttpResponse res, RouteData routeData)
  {
    return res.WriteAsync("Horse saved!");
  }

  private Task GetThing(HttpRequest req, HttpResponse res, RouteData routeData)
  {
    var thing = routeData.As<string>("type");
    return res.WriteAsync($"You asked for {WebUtility.HtmlEncode(thing)}");
  }
}

POST /horse will return 405 Method not allowed, because the GET {type} route is selected.
Switching the order of the two routes will result in GET /horse not working, because the POST /horse route returns 405.

It would be nice if Carter could find the first valid route (both correct method and route pattern match) to execute.

Generating URIs for Routes

Ability to generate a URI for another Botwin route? Similar to Nancy.Linker. This would likely need to give name to the route so you can resolve it.

My primary usage for this is hypermedia and returning URIs for relevant routes.

Add support for camelCasing in DefaultJsonResponseNegotiator

The default Json.Net casing strategy is PascalCasing, which is counter to what a lot of json-based API consumers expect. It'd be nice to easily switch to camelCasing instead.

https://www.newtonsoft.com/json/help/html/NamingStrategyCamelCase.htm

I have a local implementation within my project, but I could pull it out into a Botwin pull request if desired. It's pretty simple and just accepts IContractResolver in the contructor of the negotiator, which I then use in the SerializeObject call.

Profile Carter

After the lovely @benaadams added Carter to the TechEmpower perf tests https://github.com/TechEmpower/FrameworkBenchmarks MVC was fractionally faster on plain text.

After reading the results at 9am they sent me down a nasty path of drug and alcohol abuse. I had an existential crisis and questioned my sanity. I didn't listen to friends and ignored their advice. Luckily I passed out the other side a better man. By 9.30am I was back ready to take on the beast.

So we need to run some profiling to see if we can see any hot spots in the code because in theory Carter should be faster, it's just a wrapper over RoutingMiddleware.

LET'S DO THIS!

Obtaining Request CancellationToken

I'm not sure how to obtain a CancellationToken in my request handler. For example:

        public ActionsModule(ILinnApiActions linnApiProxy)
        {
            this.Post(
                "/ifttt/v1/actions/turn_off_all_devices",
                async (req, res, routeData) =>
                    {
                        var id = await linnApiProxy.TurnOfAllDevices(req.GetAccessToken(), CancellationToken.None);
                        var resource = new[] { new ActionResponseResource { Id = id } };
                        await res.AsJson(new DataResource<ActionResponseResource[]>(resource));
                    });
        }

I'm having to pass in a CancellationToken.None to my proxy rather than one attached to the request.

Ideally, I'd like to be able to use a CancellationToken handed in along with the req, res and routeData, is there any way I can get hold of the token?

As<T> throws InvalidCastException on Nullable types

When using As<T> and AsMultiple<T> extension methods on IQueryCollection or RouteData with Nullable data-types as T (e.g. int?), an InvalidCastException is thrown.

This is due the framework unable to cast int to int? (same for other Nullable<T>) directly when using Convert.ChangeType.

Route Level security

Was thinking of something like this.

Thoughts?

this.Get("/", async ctx => await this.RequiresAuthentication(async innerctx => await innerctx.WriteAsync("authed")));

Separate BindAndValidate so Validate can be called on its own

Currently, the BindAndValidate method does exactly what it says, but there is no way to validate the payload without binding it.

This should just be a case of introducing a public Validate method, can calling that method from BindAndValidate

routeData.As<Guid>("id") thrown Invalid cast exception

this.Get("/stock/{id:guid}", async (req, res, routeData) =>
{
     var myid = routeData.As<Guid>("id");  //thrown Invalid cast exception
     await res.Negotiate(myid);
});

There has been an error
System.InvalidCastException: Invalid cast from 'System.String' to 'System.Guid'.
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at coster.api.ExRouteDataExtensions.AsGuid(RouteData routeData, String key) in
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Carter.CarterExtensions.<>c__DisplayClass1_0.<b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.d__6.MoveNext()

Remove Travis

Remove travis.yml integration as we now have appveyor

Sample code does not work

I am hitting a rather strange issue :

 var assemblyProvider = provider.GetService<IAssemblyProvider>();
   if (assemblyProvider == null)
            {
                services.AddSingleton<IAssemblyProvider, AssemblyProvider>();
               
            }
 assemblyProvider = provider.GetService<IAssemblyProvider>();

returns a null always. I have tried running the sample and blows up with a NullReferenceException in services.AddBotwin();
It only work if you do this :

  services.AddSingleton<IAssemblyProvider, AssemblyProvider>();
  services.AddBotwin();

I will update the sample and shoot a PR

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.