All About ASP.NET and ASP.NET Core 2 Hosting BLOG

Tutorial and Articles about ASP.NET and the latest ASP.NET Core

ASP.NET Core Hosting - ASPHostPortal :: How to Integrate Stripe Payment with ASP.NET Core

clock July 15, 2019 11:53 by author Jervis

Stripe is a service for doing online payments. It's easy to get started with it because they take care of both the frontend and the backend. The frontend scripting is available for many different frameworks like React, Android, IOS and of course JavaScript and with different levels of customization. The backend is also available in almost any backend framework one could wish for, like .NET. We will discuss the different options for a JavaScript frontend, the .NET package for Stripe as the title suggests, and lastly, the use of Webhooks.

Choosing a frontend option

The frontend uses tokens to represent the card’s information. It does so by preventing the default submission of the form and creating a token of the card’s information by contacting the Stripe servers. This ensures you never need to handle personal card information on your server. It is recommended to use a SSL certificate on the payment site to ensure no one can intercept the information that the client script sends.

Stripe.js is the most basic and lightweight option and the one most customizable. It supplies a clean multipart input where the user can fill in their card nr., expiration date and CVC nr. The input instantly detects the card type and validates of the card nr. and expiration date. You can read more about the integration of Stripe.js in Stripe's guide.

 

 

Secure payments

 

From the 4th of September 2019 new security standards will apply for online payments in the EU. So, if you are targeting an European or global customer segment, you should have this in mind. The EU council PSD2 continuously work on making online payment safer for the users. This requires payments to be done using an extra step of security. You have probably already seen such methods and the one most popularly used by different banks is 3D Secure. This is already an integrated part of the Checkout solution, but is not a supported feature in Stripe.js without performing some extra steps. To enable this for Stripe.js you will need to use a PaymentIntent.

Registering at Stripe.com

Before getting started with coding you need an account at Stripe. You get this by going to their register page and filling out the form. After you are done with this you can access the Stripe dashboard. A nice feature that we will need in this experimental test phase is the View test data, which you can enable/disable directly in the menu. This option switches between real and test payments, API keys and so on. To see and create API keys go to Developers>API keys. You have both publishable and secret keys, but you should never use the secret key on the frontend. These keys differ depending on if you are in testing mode or not, so make sure to switch to live keys when you publish your site.

 

 

The main menu in the Stripe dashboard

Creating a payment

We chose to use MVC in our example and have made a Controller for payments and associated views. We chose to use the Checkout for our frontend, because it has the simplest setup.

@{
    ViewData["Title"] = "Payment";
}

<div>
    <form action="/Payment/Processing" method="POST">
        <script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
                data-key="pk_test_YourPublishAbleKey"
                data-amount="@ViewBag.PaymentAmount"
                data-name="[My Company Name]"
                data-description="10 rubberducks for 1$ each"
                data-image=
https://stripe.com/img/documentation/checkout/marketplace.png
                data-locale="en"
                data-zip-code="true"
                data-label="Pay 10$">
        </script>
    </form>
</div>

The elements in the form are made by the referenced JavaScript, so this is all you need for the frontend for now. You can change the form to your liking by using the data attributes. An example could be to fill out the amount dynamically using a ViewModel or ViewBag as shown. Other options such as specifying the currency (US Dollars is the default) can be added as well and you can read more about this in Stripe's guide for Checkout. On submit, the form posts the inputs to the action specified, which include the strings stripeToken and stripeEmail by default. Stripe.js also creates the input named stripeToken on submit, so the frontend can easily be switched to that implementation.

To get started on the backend we first need to add the package NuGet Stripe.net to our project. We then need to define the API key that we will use and a good convention is to set this in the Configure-method in Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //Your other configuration
    StripeConfiguration.SetApiKey(Configuration["Stripe:TestSecretKey"]);
}

We refence our API key using User Secrets, but you could use App Settings or an inline string instead, if that fits your style better.

We can now make the controller for our Payment Views and the post:

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using Stripe;

namespace StripeTest.Controllers
{
    public class PaymentController : Controller
    {
        private int amount = 100;
        public IActionResult Index()
        {
            ViewBag.PaymentAmount = amount;
            return View();
        }

        [HttpPost]
        public IActionResult Processing(string stripeToken, string stripeEmail)
        {
            Dictionary<string, string> Metadata = new Dictionary<string, string>();
            Metadata.Add("Product", "RubberDuck");
            Metadata.Add("Quantity", "10");
            var options = new ChargeCreateOptions
            {
                Amount = amount,
                Currency = "USD",
                Description = "Buying 10 rubber ducks",
                SourceId = stripeToken,
                ReceiptEmail = stripeEmail,
                Metadata = Metadata
            };
            var service = new ChargeService();
            Charge charge = service.Create(options);
            return View();
        }
    }
}

We take the two parameters stripeToken and stripeEmail in our action, but could add more, like information about the billing address, if you enable that in the frontend (if you have more inputs than this, a better approach would be to use a model for the form, but for now this should be fine).

We would like to add some metadata to the charge which we will use later. This is done using a Dictionary.

We then create a ChargeCreateOption object which contains the information about the Charge, where we pass in stripeToken and the Metadata Dictionary. We then create a ChargeService and use the ChargeCreateOption to make the charge. The Status of the Charge is at first pending and will change to either succeeded or failed when the charge has been processed by Stripe.

Respond to the payment

But what now? How do we know the Status of the charge? There are two different ways of getting the Status of the charge.

We can Retrieve the charge. This is done using a ChargeService again and using the action .Get(id) which takes the id of the Charge as a parameter, which we would have to save when we create it. The status could still be pending when we make the request, so we would have to run the request multiple times in intervals to check.

We can use Webhooks. In the Dashboard, we can specify an endpoint that will be called when a Charge changes status and use this to take actions on our server when we need to and not make unnecessary requests.

We decide to use Webhooks. We first need to make the Webhook on our page. We simply add another action to our Controller:

private readonly string WebhookSecret = "whsec_OurSigningSecret";

//Previous actions

[HttpPost]
public IActionResult ChargeChange()
{
    var json = new StreamReader(HttpContext.Request.Body).ReadToEnd();

    try
    {
        var stripeEvent = EventUtility.ConstructEvent(json,
            Request.Headers["Stripe-Signature"], WebhookSecret, throwOnApiVersionMismatch: true);
        Charge charge = (Charge)stripeEvent.Data.Object;
        switch (charge.Status)
        {
            case "succeeded":
                //This is an example of what to do after a charge is successful
                charge.Metadata.TryGetValue("Product", out string Product);
                charge.Metadata.TryGetValue("Quantity", out string Quantity);
                Database.ReduceStock(Product, Quantity);
                break;
            case "failed":
                //Code to execute on a failed charge
                break;
        }
    }
    catch (Exception e)
    {
        e.Ship(HttpContext);
        return BadRequest();
    }
    return Ok();
}

We have made the Action ChargeChange which starts by reading the body that was passed with the post. We then try to construct the event and cast it to the Charge type.

Now we just need to have Stripe call our Webhook, which is done in the dashboard in the menu Developers>Webhooks and selecting Add endpoint. We then enter the path of our webhook, which would be something like:

https://mysite.com/Payment/ChargeChange/

We can also choose to have it only access our webhook for certain types of changes, like only when a it changes to charge.succeeded or charge.failed. After this you can go into the Endpoint in the list and see our Webhook Secret.

 

This is a basic start for how to work with Stripe. They have many more features we didn't get to cover, including the new PaymentIntent and the setup of Subscriptions. It's easy to imagine many more technologies that Stripe could be used together with, like SignalR for real-time response on a status update of a Charge. 



ASP.NET Core Hosting :: How to Implement Custom Error Pages in ASP.NET MVC Core

clock July 11, 2019 10:01 by author Jervis

We just recently saw people ask this questions on forums, so we decide to write short brief tutorial to make custom error pages in ASP.NET MVC Core.

So, let’s start how to implement custom error pages 404s and exceptions on this blog.

Custom error pages

Anyone know what is custom error pages? It is great looking view when something goes wrong in a production environment.

In development you would use:

app.UseDeveloperExceptionPage();

Now when an exception occurs, you will see a view that describes what went wrong and where.

But it should not be used in production as it could lead to security problems. Users might also consider your site less stable, and never visit again.

So we want to show them something that says something like:

Oops! Something went wrong. We are very sorry, please try again in a moment. If the error persists...

In case of a 404 status code, by default ASP.NET Core just returns a white page.

Instead we would like to show something like:

Hey that thing you tried to access does not exist. Please check the URL is correct.

Exception handler middleware

Let's start by handling exceptions properly.

In our application pipeline configuration, we will add the exception handler middleware:

if(!env.IsDevelopment())
{
    app.UseExceptionHandler("/error/500");
}

So what does this middleware do?

Well, put simply it:

  1. Calls the next middleware with a try-catch block
  2. If an exception is caught, the next middleware is called again with the request path set to what you gave as an argument

This re-execution means the original URL is preserved in the browser.

The controller and action that is implemented looks like this:

[Route("error")]
public class ErrorController : Controller
{
    private readonly TelemetryClient _telemetryClient;

    public ErrorController(TelemetryClient telemetryClient)
    {
        _telemetryClient = telemetryClient;
    }

    [Route("500")]
    public IActionResult AppError()
    {
        var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        _telemetryClient.TrackException(exceptionHandlerPathFeature.Error);
        _telemetryClient.TrackEvent("Error.ServerError", new Dictionary<string, string>
        {
            ["originalPath"] = exceptionHandlerPathFeature.Path,
            ["error"] = exceptionHandlerPathFeature.Error.Message
        });
        return View();
    }
}

I took a look at the source code of the exception handler middleware, and found that it sets this IExceptionHandlerPathFeature on the context before re-executing the request.

Here we access it to get the relative URL the user tried to access and the exception that occurred. We use Application Insights to track the exception.

The view is pretty simple:

@{
    ViewBag.Title = "Error occurred";
}

<h1>We have a problem</h1>

<p>Sorry, an error occurred while executing your request.</p>

Now when an exception is thrown:

  1. The exception is logged in Application Insights
  2. User gets a nice-looking view instead of a stack trace
  3. The original URL is preserved in the browser so the user can try to refresh
  4. The response comes back with a 500 status code so it is tracked as a failed request in Application Insights

Handling 404s

We also want to handle 404 status codes gracefully.

In this blog, it could happen because the URL did not map to a controller action. Or it might have, but we could not find something in the database.

404s are handled with a small middleware that is placed before the MVC middleware:

app.Use(async (ctx, next) =>
{
    await next();

    if(ctx.Response.StatusCode == 404 && !ctx.Response.HasStarted)
    {
        //Re-execute the request so the user gets the error page
        string originalPath = ctx.Request.Path.Value;
        ctx.Items["originalPath"] = originalPath;
        ctx.Request.Path = "/error/404";
        await next();
    }
});

Re-executing a request is not hard as you can see. We just call await next(); again.

Here we grab the original URL and put it in the HttpContext.Items collection.

This is the action that then handles the error in the ErrorController introduced earlier:

[Route("404")]
public IActionResult PageNotFound()
{
    string originalPath = "unknown";
    if (HttpContext.Items.ContainsKey("originalPath"))
    {
        originalPath = HttpContext.Items["originalPath"] as string;
    }
    _telemetryClient.TrackEvent("Error.PageNotFound", new Dictionary<string, string>
    {
        ["originalPath"] = originalPath
    });
    return View();
}

We track the event with a custom event in Application Insights, and include the original URL in the properties.

The view is quite simple again:

@{
    ViewBag.Title = "404";
}

<h1>404 - Page not found</h1>

<p>Oops, better check that URL.</p>

If you open the browser DevTools, you can again see we get the right status code: 404.

And the original URL is preserved in the browser.

Happy Coding!



ASP.NET Core 2.2 Hosting :: Feature Flags with ASP.NET Core 2.2

clock June 25, 2019 08:09 by author Jervis

Feature flags can be useful for changing the runtime behavior of an application with minimal impact. Having a UI to toggle the feature flags on and off is extra helpful, too!

That said, we built a library to enable this in a strongly typed fashion! Here’s what the UI looks like:

Installation

Install the RimDev.AspNetCore.FeatureFlags NuGet package.

> dotnet add package RimDev.AspNetCore.FeatureFlags

Or

PM> Install-Package RimDev.AspNetCore.FeatureFlags

Usage

You’ll need to wire up Startup.cs as follows:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using RimDev.AspNetCore.FeatureFlags;

namespace MyApplication
{
    public class Startup
    {
        private static readonly FeatureFlagOptions options = new FeatureFlagOptions()
            .UseCachedSqlFeatureProvider(@"CONNECTION_STRING_HERE");

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddFeatureFlags(options);
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseFeatureFlags(options);

            // IMPORTANT: Controlling access of the UI / API of this library is the responsibility of the user.
            // Apply authentication / authorization around the `UseFeatureFlagsUI` method as needed,
            // as this method wires up the various endpoints.
            app.UseFeatureFlagsUI(options);
        }
    }
}

Next, create feature flags like this in the ASP.NET Core assembly:

using RimDev.AspNetCore.FeatureFlags;

namespace MyApplication
{
    public class MyFeature : Feature
    {
        // Optional, displays on UI:
        public override string Description { get; } = "My feature description.";
    }
}

NoteFeatureFlagAssemblies to scan can also be configured in FeatureFlagOptions if you’d like to scan assemblies other than Assembly.GetEntryAssembly().

Now you can dependency inject any of your feature flags using the standard ASP.NET Core IoC!

public class MyController : Controller
{
    private readonly MyFeature myFeature;

    public MyController(MyFeature myFeature)
    {
        this.myFeature = myFeature;
    }

    // Use myFeature instance here, using myFeature.Value for the on/off toggle value.
}

UI

The UI wired up by UseFeatureFlagsUI is available by default at /_features. The UI and API endpoints can be modified in FeatureFlagOptions if you’d like, too.

In closing

We hope this helps you control your applications in production, enabling rapid development while controlling access to new functionality in a controlled manner – decoupled from your deployments. Enjoy!



ASP.NET Core Hosting :: How to Handle Multipart Request with JSON and File Uploads in ASP.NET Core

clock May 15, 2019 09:29 by author Jervis

Suppose we’re writing an API for a blog. Our "create post" endpoint should receive the title, body, tags and an image to display at the top of the post. This raises a question: how do we send the image? There are at least 3 options:

Embed the image bytes as base64 in the JSON payload, e.g.

{
    "title": "My first blog post",
    "body": "This is going to be the best blog EVER!!!!",
    "tags": [ "first post", "hello" ],
    "image": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
}

This works fine, but it’s probably not a very good idea to embed an arbitrarily long blob in JSON, because it could use a lot of memory if the image is very large.

Send the JSON and image as separate requests. Easy, but what if we want the image to be mandatory? There’s no guarantee that the client will send the image in a second request, so our post object will be in an invalid state.

Send the JSON and image as a multipart request.

The last approach seems the most appropriate; unfortunately it’s also the most difficult to support… There is no built-in support for this scenario in ASP.NET Core. There is some support for the multipart/form-data content type, though; for instance, we can bind a model to a multipart request body, like this:

public class MyRequestModel
{
    [Required]
    public string Title { get; set; }
    [Required]
    public string Body { get; set; }
    [Required]
    public IFormFile Image { get; set; }


public IActionResult Post([FromForm] MyRequestModel request)
{
    ...
}

But if we do this, it means that each property maps to a different part of the request; we’re completely giving up on JSON.

There’s also a MultipartReader class that we can use to manually decode the request, but it means we have to give up model binding and automatic model validation entirely.

Custom model binder

Ideally, we’d like to have a request model like this:

public class CreatePostRequestModel
{
    [Required]
    public string Title { get; set; }
    [Required]
    public string Body { get; set; }
    public string[] Tags { get; set; }
    [Required]
    public IFormFile Image { get; set; }
}

Where the TitleBody and Tags properties come from a form field containing JSON and the Image property comes from the uploaded file. In other words, the request would look like this:

POST /api/blog/post HTTP/1.1
Content-Type: multipart/form-data; boundary=AaB03x  

--AaB03x
Content-Disposition: form-data; name="json"
Content-Type: application/json  

{
    "title": "My first blog post",
    "body": "This is going to be the best blog EVER!!!!",
    "tags": [ "first post", "hello" ]
}
--AaB03x
Content-Disposition: form-data; name="image"; filename="image.jpg"
Content-Type: image/jpeg  

(... content of the image.jpg file ...)
--AaB03x

Fortunately, ASP.NET Core is very flexible, and we can actually make this work, by writing a custom model binder.

Here it is:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json; 

namespace TestMultipart.ModelBinding
{
    public class JsonWithFilesFormDataModelBinder : IModelBinder
    {
        private readonly IOptions<MvcJsonOptions> _jsonOptions;
        private readonly FormFileModelBinder _formFileModelBinder; 

        public JsonWithFilesFormDataModelBinder(IOptions<MvcJsonOptions> jsonOptions, ILoggerFactory loggerFactory)
        {
            _jsonOptions = jsonOptions;
            _formFileModelBinder = new FormFileModelBinder(loggerFactory);
        } 

        public async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
                throw new ArgumentNullException(nameof(bindingContext)); 

            // Retrieve the form part containing the JSON
            var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.FieldName);
            if (valueResult == ValueProviderResult.None)
            {
                // The JSON was not found
                var message = bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(bindingContext.FieldName);
                bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, message);
                return;
            } 

            var rawValue = valueResult.FirstValue; 

            // Deserialize the JSON
            var model = JsonConvert.DeserializeObject(rawValue, bindingContext.ModelType, _jsonOptions.Value.SerializerSettings); 

            // Now, bind each of the IFormFile properties from the other form parts
            foreach (var property in bindingContext.ModelMetadata.Properties)
            {
                if (property.ModelType != typeof(IFormFile))
                    continue; 

                var fieldName = property.BinderModelName ?? property.PropertyName;
                var modelName = fieldName;
                var propertyModel = property.PropertyGetter(bindingContext.Model);
                ModelBindingResult propertyResult;
                using (bindingContext.EnterNestedScope(property, fieldName, modelName, propertyModel))
                {
                    await _formFileModelBinder.BindModelAsync(bindingContext);
                    propertyResult = bindingContext.Result;
                } 

                if (propertyResult.IsModelSet)
                {
                    // The IFormFile was sucessfully bound, assign it to the corresponding property of the model
                    property.PropertySetter(model, propertyResult.Model);
                }
                else if (property.IsBindingRequired)
                {
                    var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName);
                    bindingContext.ModelState.TryAddModelError(modelName, message);
                }
            } 

            // Set the successfully constructed model as the result of the model binding
            bindingContext.Result = ModelBindingResult.Success(model);
        }


    }
}

To use it, just apply this attribute to the CreatePostRequestModel class above:

[ModelBinder(typeof(JsonWithFilesFormDataModelBinder), Name = "json")]}
public class CreatePostRequestModel

This tells ASP.NET Core to use our custom model binder to bind this class. The Name = "json" part tells our binder from which field of the multipart request it should read the JSON (this is the bindingContext.FieldName in the binder code).

Now we just need to pass a CreatePostRequestModel to our controller action, and we’re done:

[HttpPost]
public ActionResult<Post> CreatePost(CreatePostRequestModel post)
{
    ...
}

This approach enables us to have a clean controller code and keep the benefits of model binding and validation. It messes up the Swagger/OpenAPI model though, but hey, you can’t have everything!



ASP.NET Core Hosting :: How to Produce HTTP Response in ASP.NET Core Outside of MVC Controllers

clock March 26, 2019 13:05 by author Jervis

ASP.NET Core 2.1 introduced support for a little (or, should I say, not at all) documented feature called IActionResultExecutor<T>. It allows us to use some of the action results -those that we are used to from MVC controllers – outside of the controller context, so for example from a middleware component.

Controller helper methods

The most important of the ActionResult family is the ObjectResult – which internally handles content negotiation – so determines what media type is suitable for the response, and serializes the response accordingly using the selected formatter (JSON, XML, Protobuf or whatever you support). The typical controller, using an ObjectResult might look like this:

[HttpGet("contacts")]
public IActionResult Get()
{
    var contacts = new[]
    {
        new Contact { Name = "Jervis", City = "Dallas" },
        new Contact { Name = "Not Jervis", City = "Not Dallas" }
    };
    // will do content negotiation
    return new ObjectResult(contacts);
}

We could also return a POCO directly from the action, but the framework would still end up using ObjectResult to process that, so ultimately it is still the same thing.

[HttpGet("contacts")]
public IEnumerable<Contact> Get()
{
    var contacts = new[]
    {
        new Contact { Name = "Jervis", City = "Dallas" },
        new Contact { Name = "Not Jervis", City = "Not Dallas" }
    };
    // will do content negotiation
    return contacts;
}

Finally, and this is what this post is about, you could replace our usage of ObjectResult, or the POCO, with a call to Ok() on the base controller:

[HttpGet("contacts")]
public IActionResult Get()
{
    var contacts = new[]
    {
        new Contact { Name = "Jervis", City = "Dallas" },
        new Contact { Name = "Not Jervis", City = "Not Dallas" }
    };
    // will do content negotiation
    return Ok(contacts);
}

This is really still the same as before, because Ok() actually uses ObjectResult under the hood, it’s just expressed in a slightly different way. Now, if you have done any work with MVC controllers, I am sure you are used to those helper methods that are there on the the framework’s ControllerBase, like our Ok() or other – for example Unauthorized() or File(), to name just two of them.

They come in many, many variants (the base controller is 2700+ lines of code…), and are used as shortcuts into the many ActionResults that the framework offers. You can find a full list here. From my experience with various ASP.NET Core MVC projects, I would say that these helper methods are extremely popular among developers – they make the code very concise. They also hide certain complexity level of dealing with producing the HTTP responses, yet still make it very obvious to understand what is going on.

Controller feeling, without a controller

What we recently introduced into WebApiContrib.Core is a similar set of methods as found on the base controller, but ported as extension methods on top of HttpContext. This is done as a combination of IActionResultExecutor<T> features and “manual” response creation. Meaning we either mimic the behavior of the method from base controller by manually crafting the HTTP response, setting headers, status codes and so on, or we really reach into the IActionResultExecutor<T> infrastructure and invoke the relevant action result from the MVC framework.

The end result is very neat, and you get very similar helper method set that you can now enjoy from your non-controller code – primarily middleware but potentially some other places too.

So imagine you are creating a “lightweight” HTTP endpoint using the new Endpoint Routing feature. You can now use the new helper methods to produce the response. Here is a full sample application setup:

public static async Task Main(string[] args) =>
    await WebHost.CreateDefaultBuilder(args)
        .ConfigureServices(s =>
        {
            s.AddRouting();            

            // necessary to wire in ActionResults
            // and content negotiation
            // you can manually register other formatters here
            // for example Messagepack or Protobuf
            s.AddMvc(); 

            // note: due to the current state of ASP.NET Core 3.0 (preview3)
            // you need to manually call: s.AddMvc().AddNewtonsoftJson()
            // to use JSON formatter. This will be fixed in the future in the framework
        })
        .Configure(app =>
        {
            app.UseRouting(r =>
            {
                r.MapGet("contacts", async context =>
                {
                    var contacts = new[]
                    {
                        new Contact { Name = "Jervis", City = "Dallas" },
                        new Contact { Name = "No Jervis", City = "Not Dallas" }
                    }; 

                    // from WebApiContrib.Core
                    // will do content negotiation
                    await context.Ok(contacts);
                });
            });
        }).Build().RunAsync();

In order to make this work, the only thing that is needed, is that it’s necessary to have a call to services.AddMvc() in the DI container setup, as the action result and the executor infrastructure is bootstrapped there.

Other than that, this Ok() extension method on HttpContext will behave exactly the same as the Ok() on base controller – including performing the full content negotiation. The example uses endpoint routing from ASP.NET Core 3.0, but it would work from any place in ASP.NET Core request processing pipeline, for example with an IRouter in ASP.NET Core 2.1 or any middleware.

Another example we could quickly look at here, is returning a file stream – instead of dealing with it manually, including all the complexity of async reading of the stream or stuff related to Content-Disposition headers and so on, we can simply use an extension method now:

app.UseRouting(r =>
{
    r.MapGet("download", async context =>
    {
        // some file path
        var path = Path.GetFullPath(Path.Combine("files", "myfile.pdf")); 

        // from WebApiContrib.Core
        await context.PhysicalFile(path, "application/pdf");
    });
});

In this particular case, the helper method would end up using an action result, a PhysicalFileActionResult, which will take care of reading the file in a non-blocking way and make sure all HTTP response details are correctly handled. And just like before, PhysicalFile() mimics a corresponding method from the MVC base controller.

The full list of the available extension methods can be found below. And I really encourage you to try them – they are part of WebApiContrib.Core 2.2.0.

Task Accepted(this HttpContext c, Uri uri, object value);
Task Accepted(this HttpContext c, string uri, object value);
Task Accepted(this HttpContext c, string uri);
Task Accepted(this HttpContext c, Uri uri);
Task Accepted(this HttpContext c, object value);
Task Accepted(this HttpContext c);
Task BadRequest(this HttpContext c);
Task BadRequest(this HttpContext c, object error);
Task BadRequest(this HttpContext c, ModelStateDictionary modelState);
Task Conflict(this HttpContext c, object error);
Task Conflict(this HttpContext c, ModelStateDictionary modelState);
Task Conflict(this HttpContext c);
Task Content(this HttpContext c, string content, MediaTypeHeaderValue contentType);
Task Content(this HttpContext c, string content, string contentType, Encoding contentEncoding);
Task Content(this HttpContext c, string content, string contentType);
Task Content(this HttpContext c, string content);
Task Created(this HttpContext c, string uri, object value);
Task Created(this HttpContext c, Uri uri, object value);
Task File(this HttpContext c, string virtualPath, string contentType);
Task File(this HttpContext c, Stream fileStream, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task File(this HttpContext c, string virtualPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, string virtualPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, string virtualPath, string contentType, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName);
Task File(this HttpContext c, Stream fileStream, string contentType, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, byte[] fileContents, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, byte[] fileContents, string contentType, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task Forbid(this HttpContext c);
Task LocalRedirect(this HttpContext c, string localUrl);
Task LocalRedirectPermanent(this HttpContext c, string localUrl);
Task LocalRedirectPermanentPreserveMethod(this HttpContext c, string localUrl);
Task LocalRedirectPreserveMethod(this HttpContext c, string localUrl);
Task NoContent(this HttpContext c);
Task NotFound(this HttpContext c, object value);
Task NotFound(this HttpContext c);
Task Ok(this HttpContext c);
Task Ok(this HttpContext c, object value);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, bool enableRangeProcessing);
Task Redirect(this HttpContext c, string url);
Task RedirectPermanent(this HttpContext c, string url);
Task RedirectPermanentPreserveMethod(this HttpContext c, string url);
Task RedirectPreserveMethod(this HttpContext c, string url);
Task StatusCode(this HttpContext c, int statusCode);
Task StatusCode(this HttpContext c, int statusCode, object value);
Task Unauthorized(this HttpContext c);
Task Unauthorized(this HttpContext c, object value);
Task UnprocessableEntity(this HttpContext c, object error);
Task UnprocessableEntity(this HttpContext c, ModelStateDictionary modelState);
Task UnprocessableEntity(this HttpContext c);
Task ValidationProblem(this HttpContext c, ValidationProblemDetails descriptor);
Task ValidationProblem(this HttpContext c, ModelStateDictionary modelStateDictionary);
Task WriteActionResult<TResult>(this HttpContext c, TResult result) where TResult : IActionResult;



ASP.NET Core Hosting :: How to Setup Project in IOptionsSnapshot using ASP.NET and Reload Technique

clock March 19, 2019 09:49 by author Jervis

In this article, I want to explore IOptionsSnapshot and show how to work with IOptionsSnapshot in ASP.NET Core 1.1.

We will use the dot-net CLI to create a new project and configure it using the reload technique in combination with IOptionsSnapshot.

Make sure you are using at least ASP.NET Core 1.1. Get started by creating a new folder that you want to work in, opening a console there and typing dotnet new mvc and dotnet restore to get the project in a starting position. Then open up a console and type code. to start Visual Studio Code on the current level.

You should now see all the files and folders of your project. We can now go ahead and create a typed class which represents the configuration we want to work with. In this case, this is just a file with a name property.

Config
  └── myConfig.json
  Controllers └── ...
  Views └── ...
  wwwroot └── ... ...
  Program.cs
  Startup.cs

myConfig.json

{
    "Person": {
        "Firstname": "John Doe"
    }
}

This leads us to the class

public class Person 
{
    public string Firstname { get; set; }
}

Config 
  └── myConfig.json 
  Controllers └── ... 
  Views └── ... 
  wwwroot └── ... ... 
  Program.cs 
  Startup.cs

which represents our configuration in our application.

We have to modify our constructor of the Startup.cs file a bit to load this new file:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

This then becomes: 

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddJsonFile($"config/myConfig.json", optional: false, reloadOnChange: true)
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

Pay attention to the realoadOnChange: true because that is what we are reaching out for.

So now that we loaded the file we need to add it to our configuration which is used in our app. Let's do this by adding the statement in the ConfigureServices -Method:

public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.Configure<Person>(Configuration.GetSection("Person"));
}

Here we are mapping our values in JSON to a typed class called “Person.”

Now, this configuration is available through dependency injection and we can use it in our controllers!

public class HomeController : Controller 
{
    private readonly Person _person;
    public HomeController(IOptionsSnapshot<Person> person)
    {
        _person = person.Value;
    }
}

Pay attention to the “IOptionsSnapshot” we injected here which is different from the previous ASP.NET Core versions. Be sure to have the "Microsoft.Extensions.Options": "1.1.0" package installed and that you are using ASP.NET Core 1.1. We can now inject the IOptionsSnapshot<T> in our controller and use its value. For testing, we save the Firstname in the ViewData displaying it afterward.

namespace WebApplication6.Controllers
{
    public class HomeController : Controller
    {
        private readonly Person _person;
        public HomeController(IOptionsSnapshot<Person> person)
        {
            _person = person.Value;
        }
        public IActionResult Index()
        {
            ViewData["FirstName"] = _person.Firstname;
            return View();
        }
    }
}

Index.cshtml

<h3>@(ViewData["FirstName"])</h3>

If you now start the web application via dotnet run and you change the configuration without restarting the application, hit F5 to refresh the browser and you will see the new values.



ASP.NET Core Hosting :: In Proces Hosting ASP.NET Core Applications

clock March 15, 2019 08:18 by author Jervis

We discussed about ASP.NET MVC Core 2.1 and discussed some internal details about its deployment on IIS as a reverse proxy (which is recommended), and also took a look on using Kestrel as an Edge Server. Although Kestrel is matured enough to be used as an Edge Server but still IIS is considered better option. We also saw, how ASP.NET Core requests are handled by IIS. We need to install the .NET Core Hosting bundle (download here) which adds a module named ASP.NET Core Module  (ANCM). ANCM is responsible to route the asp.net core request to Kestrel.

With ASP.NET Core 2.2, Microsoft introduced In-process hosting. This model allows us to host the asp.net core 2.2 directly inside the worker process (w3wp.exe) which is similar to earlier ASP.NET version. Let’s take a pictorial view

We can see that there is no dotnet.exe is involved here in the second part. All the components ANCM, CoreCLR and application code are loaded in same worker process.

To use the latest feature, we need to install the latest 2.2 bundle (download here) which installs the upgraded version of ANCM also referred as ANCMv2. After installation, both the modules can be seen in IIS modules section as

Why new version of ASP.NET Core Module (ANCMv2)?

Earlier the idea with ANCM to use IIS as a reverse proxy and leverage Kestrel as a backend web server (as it was not hardened enough as an edge server) but as Kestrel got all the required enhancements, MS reworked on ANCM so that IIS can be used another platform to host asp.net core applications without the need of Kestrel. ANCM got redesigned and divided in two components, a shim and a request handler.

     Shim – As the name suggests, it is a very light weight component which is continue to be installed as a global module via bundle which just work as an interface between IIS and request handler.

   Request Handler – Request Handler is now an independent component which does all the work and can be updated via nuget. We can have multiple versions of request handler side by side. It means we can have multiple application using its own request handler.

With earlier ANCM, it was available as global singleton module which is shared by all the application which is a major bottleneck in releasing newer versions as it has to support every application. With the new architecture, we also get better process management, performance enhancements and easy updates via nuget.

We have so many benefits with the new model however we have one limitation – one application pool can only host only one application (In ASP.NET Web Form/MVC we could share app pools with multiple applications) as we don’t have the concept of Application domains in CoreCLR and this feature supports to CoreCLR only.

Let’s see an example

Now I have created another sample web application application using ASP.NET Core 2.2 (used VS 2017 v15.9.4) and deployed to IIS after publishing that.

 

There is no brainer here, let’s see the processes

Just to compare with earlier version I am adding both here.

 

So we can see the difference, in first scenario (<ASP.NET Core 2.2) the application is running under dotnet.exe while in second scenario, it is running under the worker process (w3wp.exe) itself which boosts the performance significantly as we don’t have to manage the dotnet process (earlier approach could have reliability issues as well) and request doesn’t have to travel outside of the process.

ASP.NET Core 2.2 allows out of process deployment as well. When we publish our application, it generates a web.config which has following xml node

<aspNetCore processPath=”dotnet” arguments=”.\InProcApp.dll” stdoutLogEnabled=”false” stdoutLogFile=”.\logs\stdout” hostingModel=”InProcess” />

Here hosting model at the end defined as InProcess. We can change it to OutOfProcess which would be similar as earlier one and application would be running using dotnet.exe. These configuration can also be set via Visual Studio while debugging as

Go to Solution Explorer -> Right Click on project-> Debug (tab)-> Web Server settings section

Performance comparison

As mentioned above, with ASP.NET Core 2.2 allows us to host both the In-process and Out-of-process model (It is similar to earlier version). I have done sample load test using the Netling (know more about this tool here) and for out-of-process result is here

We can see that 2576 request got served per second. I changed the hosting as In-process and ran the same test again and the results are

Here we can see that request per second got increased significantly to 3742 which is approximate ~50% increase. Other data points like median, stddev also got reduced significantly. Itmay vary based on the scenario as I ran it on a developer VM and the application used was a default sample application using asp.net core 2.2 (not an empty application). However, Microsoft ran the test in performance labs where they got 4x throughput with In-process hosting.

 

Conclusion

Even kestrel was introduced with ASP.NET Core as a highly performant web server or as an alternate to IIS, it was always suggested to use IIS as frontend server in windows environment. Initially, many important features were missing in Kestrel which got added with the release of asp.net core 2.0 and 2.1, still IIS is advised to use for enterprise environment and internet facing application mainly due to security and stability reasons. There were several bottlenecks with having two different processes (w3wp.exe and dotnet.exe) and the way like port conflicts/not available, process management etc. All these can be avoided using In-process hosting model.



ASP.NET Core Hosting :: How to Access HttpContext Outside of Framework Components in ASP.NET Core

clock March 13, 2019 08:44 by author Jervis

When developing web applications with ASP.NET, it is common to end up in situations where you require access to HttpContext. This wouldn’t be anything special, but outside of the context of framework level APIs such as controllers, middleware and so on (which would always give you a way to fetch the current HttpContext), it can be tricky.

While generally speaking, HttpContext could be passed around as a regular dependency to the logical components that require it, that solution is often impractical.

Let’s have a look at how you can get a hold of HttpContext in ASP.NET Core.

HttpContextAccessor

ASP.NET Core provides a convenience interface, IHttpContextAccessor (and it’s default implementation, HttpContextAccessor) in order to simplify accessing HttpContext. It must be registered at application startup inside the IServicesCollection and once it’s there, the framework will make sure that you can inject it anywhere you need, and use it to access the current instance of HttpContext.

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

HttpContextAccessor under the hood

So how does it work? Consider piece of code you can find in any ASP.NET Core template:

    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .Build(); 

            host.Run();
        }
    }

It provides the launch point of your application. This is going to start the server and boostrap all the necessary services, including building up the request processing pipeline from your Startup class.

Internally, in the process of that bootstrapping, that code will wire in the relevant server (Kestrel) and will create an instance of HostingApplication and pass into it an implementation of IHttpContextFactory (more on that later).

HostingApplication is an implementation of IHttpApplication which exposes three methods:

    public interface IHttpApplication<TContext>
    {
        /// <summary>
        /// Create a TContext given a collection of HTTP features.
        /// </summary>
        /// <param name="contextFeatures">A collection of HTTP features to be used for creating the TContext.</param>
        /// <returns>The created TContext.</returns>
        TContext CreateContext(IFeatureCollection contextFeatures); 

        /// <summary>
        /// Asynchronously processes an TContext.
        /// </summary>
        /// <param name="context">The TContext that the operation will process.</param>
        Task ProcessRequestAsync(TContext context); 

        /// <summary>
        /// Dispose a given TContext.
        /// </summary>
        /// <param name="context">The TContext to be disposed.</param>
        /// <param name="exception">The Exception thrown when processing did not complete successfully, otherwise null.</param>
        void DisposeContext(TContext context, Exception exception);
    }

The server that we are using (say, Kestrel), on each incoming request, will use the above interface to call CreateContext and later on ProcessRequestAsync.

The former method is where IHttpContextFactory will be used to initialize HttpContext instance, and that instance will live throughout the lifetime of the HTTP request. The default implementation of IHttpContextFactory will look into the DI container, and check if IHttpContextAccessor is there. If it is, then it will “share” its HttpContext instance with the accessor.

The HttpContextAccessor will then store the HttpContext using System.Runtime.Remoting.Messaging.CallContext on desktop CLR and using System.Threading.AsyncLocal when built against .NET Standard.

If the accessor is not registered in the DI, then of course the context will not be saved anywhere. This is really important – and I have seen some questions already about that. If you just manually create an instance of HttpContextAccessor (which some people try), it will have no relationship to the HttpContextFactory or HttpContext, and the context will always be null. The accessor is merely a shortcut with a getter and setter, while all the logic of associating the HttpContext with the accessor instance is in HttpContextFactory.

And that’s basically how it works.

Injecting HttpContextAccessor

With all that set up, we could inject IHttpContextAccessor wherever we require access to the current instance of HttpContext. This of course means that your own components that rely on it, should be registered in/resolved from the IoC container too.

public class MyService
{
    private readonly IHttpContextAccessor _accessor; 

    public MyService(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    } 

    public void DoWork()
    {
        var context = _accessor.HttpContext;
        // continue with context instance
    }
}

Mimicking HttpContext.Current

One of the most infamous relicts of System.Web that is missing in ASP.NET Core is the static access to the current HttpContext.

I bet there is not a single ASP.NET developer, that, over the years, has not seen tons of programs, logic and extensions developed based on the magic and omnipresence of HttpContext.Current.

Now, trying to build your code around HttpContext.Current is really not a good idea, but I guess if you are migrating an enterprise type of app, with a lot of HttpContext.Current sprinkled around the business logic it may provide some temporary relief in terms of porting the application.

Our modern day HttpContext.Current would rely on resolving the context from IHttpContextAccessorand could look like this:

namespace System.Web
{
    public static class HttpContext
    {
        private static IHttpContextAccessor _contextAccessor; 

        public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext; 

        internal static void Configure(IHttpContextAccessor contextAccessor)
        {
            _contextAccessor = contextAccessor;
        }
    }
}

Notice, how we even placed it in System.Web namespace so that any potential migration you have is a bit easier.

We just need to add the code that will call into Configure as early as we can in the processing pipeline and pass in the IHttpContextAccessor. This can be achieved with two extension methods:

    public static class StaticHttpContextExtensions
    {
        public static void AddHttpContextAccessor(this IServiceCollection services)
        {
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        } 

        public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app)
        {
            var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
            System.Web.HttpContext.Configure(httpContextAccessor);
            return app;
        }
    }

The first one would be called from within ConfigureServices in your Startup and simply register the accessor in the DI. We have already established that this is necessary for the default IHttpContextFactory to share its instance of HttpContext correctly.

The second would be called from within Configure in your Startup, and it will make sure that our custom HttpContext.Current gets fed its IHttpContextAccessor so that it can work properly too.

And that’s it. Here is my Startup class which sets up the table for the static HttpContext.Current.

    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddHttpContextAccessor();
        } 

        public void Configure(IApplicationBuilder app)
        {
            app.UseStaticHttpContext();
            app.UseMvc();
        }
    }

And this is the rewritten example from above.

using System.Web; 

public class MyService
{
    public void DoWork()
    {
        var context = HttpContext.Current;
        // continue with context instance
    }
}

However, please think twice before going down that road.



ASP.NET Core MVC Hosting :: Dependency Injection Into Actions in ASP.NET Core MVC 2.1

clock March 4, 2019 10:15 by author Jervis

In this tutorial, we will see configuration about dependency injection into actions in ASP.NET Core MVC 2.1

The basic idea

Just in case what we are trying to achieve here is not obvious yet, here is an extremely basic layout of the problem. Let’s imagine having two separate services, with two separate implementations.

public interface IHelloService
{
    string SayHello();
}
public class HelloService : IHelloService
{
    public string SayHello() => "Hello";


public interface IGoodbyeService
{
    string SayGoodbye();


public class GoodbyeService : IGoodbyeService
{
    public string SayGoodbye() => "Bye";
}

For the sake of completeness, let’s also include a basic request DTO. It’s not needed at all, but I want to have it just to be able to illustrate that injecting dependencies as parameters will not interfere with regular binding process.

public class RequestDto
{
    public string Name {get; set;}
}

We’d like to consume them in a controller, but rather than doing this

public class GreetController : ApiController
{
    private readonly IHelloService _helloService;
    private readonly IGoodbyeService _goodbyeService; 

    public GreetController(IHelloService helloService, IGoodbyeService goodbyeService)
    {
        _helloService = helloService;
        _goodbyeService = goodbyeService;
    } 

    [HttpPost("hello")]
    public string Post(RequestDto input)
        => _helloService.SayHello() + " " + input.Name;        

    [HttpPost("bye")]      
    public string Post(RequestDto input)
        => _goodbyeService.SayGoodbye() + " " + input.Name;
}

I would want to see the following setup:

[Route("api/[controller]")]
[ApiController]
public class GreetController : ApiController
{
    [HttpPost("hello")]
    public ActionResult<string> Post(RequestDto input, IHelloService svc)
        => svc.SayHello() + " " + input.Name;        

    [HttpPost("bye")]      
    public ActionResult<string> Post(RequestDto input, IGoodbyeService svc)
        => svc.SayGoodbye() + " " + input.Name;
}

Note that the [ApiController] and ActionResult<T> were introduced in ASP.NET Core 2.1. They will actually impact the discussion, but more on that later.

Injection into actions

As I mentioned earlier, contrary to i.e. ASP.NET Web API, where we needed to override some internal components to make this, the basic scenario laid out here works out of the box in ASP.NET Core. The only caveat is that you have to decorate the parameter that you’d like to inject (resolve from the DI container) with a [FromServices] attribute.

[HttpPost("hello")]
public ActionResult<string> Post(RequestDto input, [FromServices]IHelloService svc)
     => svc.SayHello() + " " + input.Name;

We could finish the discussion here, as the goal is pretty much achieved. For example, I could issue a following request

curl -X POST \
  https://localhost:5001/api/greet/hello \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
"name":"jervis"
}'

and then get the following response:

Hello jervis

However, it is still possible to get rid of the [FromServices] attribute by establishing a reasonable convention. For example, we could easily detect at application startup, when the application model is composed, if a parameter is an interface, and if that’s the case, we would resolve it from the container.

In the past, in ASP.NET Core 2.0, this could be achieved by building an IApplicationModelConvention, registering it, iterating over all discovered controller, then over their actions and then over their parameters, and inspecting those. Then, marking the parameters we’d want to resolve from the DI container with a BindingSource.Services.

This would partially work in ASP.NET Core 2.1 too, however only if you wouldn’t use the new ApiControllerAttribute feature. That feature would validate whether your actions doesn’t inject more than one unannotated complex parameter into an action (which is essentially what we are doing) and throw an exception if that’s the case. That validation happens before our convention would run. The reason for this is that the ApiControllerAttribute feature is implemented as an IApplicationModelProvider which does similar stuff too IApplicationModelConvention – but providers run before the conventions do. So to address that we’d implement a custom provider instead.

public class ActionDependencyModelProvider : IApplicationModelProvider
{
    public int Order => -901; 

    public void OnProvidersExecuted(ApplicationModelProviderContext context)
    {
    } 

    public void OnProvidersExecuting(ApplicationModelProviderContext context)
    {
        foreach (var controllerModel in context.Result.Controllers)
        {
            foreach(var actionModel in controllerModel.Actions)
            {
                foreach(var parameterModel in actionModel.Parameters)
                {
                    if (parameterModel.ParameterType.IsInterface)
                    {
                        parameterModel.BindingInfo = new BindingInfo()
                        {
                            BindingSource = BindingSource.Services
                        };
                    }
                }
            }
        }
    }
}

Two implementation notes here – I used the Order value of -901 – that’s because the provider responsible for ApiControllerAttribute uses the value -900 and we’d want to run before it. Another thing is that we check for a parameter being an interface to resolve it from the DI container. Of course you are free to establish your own convention here (especially as it’s perfectly reasonable to have non-interface based dependencies). For example you could have a simple convention that 1st parameter would be bound from body and next ones from the container, or a convention where the assembly from which the Type comes dictates whether it’s a request model or a dependency, or simply some naming convention.

You’d just have to register this provider at startup, and that’s it. In our case the Startup registrations now look like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHelloService, HelloService>();
    services.AddSingleton<IGoodbyeService, GoodbyeService>(); 

    services.TryAddEnumerable(
        ServiceDescriptor.Transient<IApplicationModelProvider, ActionDependencyModelProvider>());
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

And that’s it – you can now use your dependencies exactly as we defined it earlier:

[Route("api/[controller]")]
[ApiController]
public class GreetController : ApiController
{
    [HttpPost("hello")]
    public ActionResult<string> Post(RequestDto input, IHelloService svc)
        => svc.SayHello() + " " + input.Name;        

    [HttpPost("bye")]      
    public ActionResult<string> Post(RequestDto input, IGoodbyeService svc)
        => svc.SayGoodbye() + " " + input.Name;
}



ASP.NET Core 3 Hosting :: ASP.NET Core 3 Preview

clock February 26, 2019 06:50 by author Jervis

ASP.NET Core 3.0 App with .NET Core 3.0 preview 2 release

Before we create the application, first we need to install Visual Studio 2019 and .NET Core 3.0. Let’s first install .NET Core 3.0 SDK.

Installing .NET Core 3.0

To download .NET Core 3.0 preview 2, visit this link. Based on your platform, download the appropriate installer. Once the download is complete, run the installer to install .NET Core 3.0 on your system. The .NET Core 3.0 preview installation will not impact your existing .NET Core version installation.

Installing Visual Studio 2019 Preview

To install Visual Studio 2019 preview, download the installer from this location. Don’t worry. Visual Studio and Visual Studio “Preview” can be installed side-by-side on the same device. It will have no impact on your current stable VS installation.

Visual Studio 2019 offers a completely new project creation experience. Once the installation is complete, let’s open the Visual Studio 2019 preview and create the ASP.NET Core 3.0 app. Select the ASP.NET Core Web Application template.

When you click Ok, you will get the following prompt. Select ASP.NET Core 3.0 and choose the MVC template.

The Visual Studio will create an ASP.NET Core 3.0 based MVC project. The solution structure looks similar to the previous version of ASP.NET Core. However, there is one change with respect to dependencies reference, which is the Microsoft.AspNetCore.Mvc.NewtonsoftJson nuget package.

 

ASP.NET Core shared framework (Microsoft.AspNetCore.App) will only contain first-party assemblies that are fully developed, supported, and serviceable by Microsoft. As part of this change, the following sub-components will be removed from shared framework.

  • Json.NET (Newtonsoft.Json)
  • Entity Framework Core (Microsoft.EntityFrameworkCore.*)
  • Microsoft.CodeAnalysis (Roslyn)

The project file is now targeting to .NET Core 3.0 and also has a reference of Microsoft.AspNetCore.Mvc.NewtonsoftJson package.

<Project Sdk="Microsoft.NET.Sdk.Web"> 

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup> 

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0-preview-19075-0444" />
  </ItemGroup> 

</Project>

Let’s take a look at the code level changes.

1. Open the Program.cs and you will see the following code. The ASP.NET Core 3.0 templates use Generic Host. Previous versions used Web Host.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    } 

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

The goal of the Generic Host is to decouple the HTTP pipeline from the Web Host API to enable a wider array of host scenarios. Messaging, background tasks, and other non-HTTP workloads based on the Generic Host benefit from cross-cutting capabilities, such as configuration, dependency injection (DI), and logging.

The above code uses webBuilder which is a type of IWebHostBuilder interface used with WebHostBuilder. But it will be deprecated and eventually its functionality will be replaced by HostBuilder, though the interface will remain.

The biggest difference between WebHostBuilder and HostBuilder is that you can no longer inject arbitrary services into your Startup.cs. Instead, you will be limited to the IHostingEnvironment and IConfiguration interfaces. This removes a behavior quirk related to injecting services into Startup.cs before the ConfigureServices method is called.

2. As mentioned earlier, Json.NET is removed from the shared framework and now needs to be added as a package. Open Startup.cs and take a look at ConfigureServices method

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    }); 

    services.AddMvc()
        .AddNewtonsoftJson();
}

3. There are some updates to EndPoint routing introduced with ASP.NET Core 2.2. Endpoint routing allows frameworks like MVC as well as other routable things to mix with middleware in a way that hasn’t been possible before. With this, routing decisions can occur earlier into the pipeline so that incoming requests can be mapped to their eventual endpoint before MVC is even invoked. This is now present in the project templates in 3.0.0-preview-2 (Startup.cs -> Configure()).

app.UseRouting(routes =>
{
     routes.MapApplication();
     routes.MapControllerRoute(
         name: "default",
         template: "{controller=Home}/{action=Index}/{id?}");
});

Here, the app.UseRouting() call adds a new Endpoint Routing middleware. The UseRouting replaces many of the features that were implemented inside UseMvc() in the past. The MapApplication() brings in MVC controllers and pages for routing and MapControllerRoutedefines the default route.

That’s it for now

Summary

ASP.NET Core 3.0 will bring some code breaking changes and some of them are available with this preview 2 release. The change regarding ASP.NET Core shared framework to include only those libraries which are developed, supported, and serviceable by Microsoft will definitely reduce the application size by a few bytes. It is the right time to play around ASP.NET Core 3.0 and expect some more changes when the final version comes out.



Cheap ASP.NET 4.5 Hosting

We’re a company that works differently to most. Value is what we output and help our customers achieve, not how much money we put in the bank. It’s not because we are altruistic. It’s based on an even simpler principle. "Do good things, and good things will come to you".

Success for us is something that is continually experienced, not something that is reached. For us it is all about the experience – more than the journey. Life is a continual experience. We see the Internet as being an incredible amplifier to the experience of life for all of us. It can help humanity come together to explode in knowledge exploration and discussion. It is continual enlightenment of new ideas, experiences, and passions


Author Link

 photo ahp banner aspnet-01_zps87l92lcl.png

 

Corporate Address (Location)

ASPHostPortal
170 W 56th Street, Suite 121
New York, NY 10019
United States

Tag cloud

Sign in