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 4.6 Hosting :: How to Solve Error "Could not create SSL/TLS secure channel"

clock July 18, 2019 07:01 by author Jervis

Yeap, ASP.NET Core 2.2 has been released and ASP.NET Core will release soon. But, there are many other users that still using previous ASP.NET version like ASP.NET 4.6. I just found in forum that people encounter error setting up SSL/TLS in .NET 4.6. The following is the error message

System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel

With SSL3 and TLS1.0 being deprecated, I figured this had to do with a version mismatch between the browser and the server. In fact, that was true. The reason why the security protocol didn’t default to TLS 1.2 in my application is because it was running on .NET 4.6.2, and in .NET 4.6x there is no default set for the security protocol. Also, it was running on a version of Windows (2012 R2) that didn’t have newer versions of TLS enabled by default.

One solution to this is to recompile your website either specifying a default or targeting .NET 4.7, which does have a default value of SecurityProtocolType.SystemDefault. According to the Microsoft .NET documentation, this setting “allows .NET Framework networking APIs based on SslStream (such as FTP, HTTP, and SMTP) to inherit the default security protocols from the operating system or from any custom configurations performed by a system administrator”. In my case, that may not have helped since the OS didn’t have TLS1.2 enabled.

Strong Cryptography Mode

I wasn’t able to recompile the application at the time, anyway, and so needed to find another way to fix the issue by reconfiguring the OS. In the end, I was able to fix the issue by enabling something called “strong cryptography mode” in Windows on the web server, which you can read more background about here.

To make the change, I simply had to run the two commands below in an elevated PowerShell prompt on the server. The first command is for x64 .NET and the second for x86 .NET.

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord

After those commands are run, you can run the following command to verify the setup:

[Net.ServicePointManager]::SecurityProtocol

This will list the enabled SSL/TLS protocols, which in my case now includes TLS12 (that is, TLS 1.2).

Finally, I simply reset IIS to restart my application, and I now no longer get the “Could not create SSL/TLS secure channel” exception and the API longer returns HTTP 500 responses!

Hope this helps!



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!



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