Intro to Ravencoin Development – Part 2 – Refactoring

All code described below has been checked into github. Feel free to clone and go along with it as you read.

I wanted to choose something more exciting than refactoring and cleaning up for part 2, but what we built in Part 1 is “quick and dirty”. Some of the design principles are simply not there if we want to continue to expand upon this project.

When I decided to refactor what we wrote in the last lesson, I wanted to get these features out of it:

  • Building an ApplicationCore class library specifically for business logic
    • This allows for code to be re-used by other applications
      • For example, last lesson we built a web api. What if we wanted to build a command line application or a winforms (ugh) application? We wouldn’t want to write the same code twice, that’s bad practice.
    • Enables us to make unit tests
  • Write fully async/awaited code
    • Allows us to run calls asynchronously. No blocking waiting for other tasks to complete
  • Create common command/response/connection classes to pass back and forth
    • Stored in the ApplicationCore class library for reusability
  • The client (web, console, etc) application should only be passing expected variables for the RPC function using native types
    • i.e. the web client should not have to worry about creating a json object to pass to the rpc server
  • Lots of comments
    • Including summary/param/returns comments, so Intellisense tells you what the function expects
  • Using dependency injection to pass our connection details
  • Logging
  • A bit more consistent naming (PascalCase for Functions, camelCase for Properties)

Creating a new Application

Using the existing project, right click “Solution ‘RavcencoinApiExample'” at the top of solution explorer and go to Add -> New Project

Choose Class library:

Click Next, then name it “Ravencoin.ApplicationCore”, Click next again, and make sure. .Net Core 3.1 is selected. Click Create.

Next, Rename the existing Web project to “Ravencoin.Web”. When this is complete, you should have two items in your project similar to this:

We’ll also need to add Newtonsoft.Json to this project. Right clock Ravencoin.ApplicationCore -> Manage NuGet Packages. Click Browse and search for Newtonsoft.Json and Install it.


Create Folders in Ravencoin.ApplicationCore

We’re going to organize things a bit better.

Right click Ravencoin.ApplicationCore and go to Add => New Folder.

Name the new folder “Models”

Do this again for two more folders, “BusinessLogic” and “RpcConnections”

It should look like this after you’re done:


Creating The RPC Connection class

Right click the RpcConnections folder, and go to Add->Class. Name it “Ravencore.cs”. This will be the class that handles the connection to the Ravencoin RPC node. This time we’re using asynchronous code, returning a Task<T> response, and doing some minor exception handling. Also notice we’re passing two objects:

  • ServerCommand object (similar to our previous RavencoinRequest class)
  • ServerConnection object (stores hostname/port/username/password)

And it will return a ServerResponse object.

using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Ravencoin.ApplicationCore.Models;
using System.Threading.Tasks;

namespace Ravencoin.ApplicationCore.RpcConnections
{
    class RavenCore
    {
        public static async Task<ServerResponse> Connect(ServerCommand command, ServerConnection connection)
        {
            HttpClient client = new HttpClient();
            Uri baseUri = new Uri($"http://{connection.host}:{connection.port}");
            client.BaseAddress = baseUri;
            client.DefaultRequestHeaders.Clear();
            client.DefaultRequestHeaders.ConnectionClose = true;
            client.DefaultRequestHeaders.Add("Accept", "application/json");

            //Set up authentication
            var authenticationString = $"{connection.username}:{connection.password}";
            var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));

            //Set up the body of the message.
            //This adds the authorization header and encoding the RavencoinRequest object into json.
            var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/");
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString);

            //Set the content of the body
            requestMessage.Content = new StringContent(JsonConvert.SerializeObject(command), Encoding.UTF8, "application/json");

            // Try to send the message, if we get an error response as such with details
            try
            {
                //make the request
                HttpResponseMessage httpresponse = await client.SendAsync(requestMessage);

                //ensure we get a good response
                httpresponse.EnsureSuccessStatusCode();

                ServerResponse response = new ServerResponse {
                    statusCode = System.Net.HttpStatusCode.OK,
                    responseContent = await httpresponse.Content.ReadAsStringAsync()
                };

                return response;

            }
            catch (HttpRequestException httpEx)
            {
                ServerResponse exresponse = new ServerResponse
                {
                    statusCode = System.Net.HttpStatusCode.BadRequest,
                    errorEx = httpEx.Message
                };

                return exresponse;

            }
        }
    }
}

This will leave you with a bunch of errors, because those objects don’t exist yet.


Creating the Models

Right Click the Models folder and go to Add-> Class. Name it ServerResponse.cs. Do this again two more times for ServerCommand.cs, and ServerConnection.cs.

In ServerResponse.cs, put in:

using System.Net;
namespace Ravencoin.ApplicationCore.Models
{
    public class ServerResponse
    {
        public HttpStatusCode statusCode { get; set; }
        public string errorEx { get; set; }
        public string responseContent { get; set; }
    }
}

In ServerCommand.cs, put in:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Ravencoin.ApplicationCore.Models
{
    public class ServerCommand
    {
        [JsonProperty(PropertyName = "id")]
        public string commandId { get; set; }

        [JsonProperty(PropertyName = "method")]
        public string commandMethod { get; set; }

        [JsonProperty(PropertyName = "params")]
        public JObject commandParams { get; set; }

        [JsonProperty(PropertyName = "jsonrpc")]
        public string commandJsonRpc { get; set; }
    }
}

In ServerConnection.cs, put in:

namespace Ravencoin.ApplicationCore.Models
{ 
    public class ServerConnection
    {
        public string host { get; set; }
        public int port { get; set; }
        public string username { get; set; }
        public string password { get; set; }
    }
}

Adding Business Logic

As I said above, we should consider this class library to be akin to a driver that our application uses. It does all the heavy lifting and formatting of what we want it to do.

Let’s start with our blockchain logic. Right click the BusinessLogic folder, Go to Add -> Class. Name it Blockchain.cs.

Add this code to Blockchain.cs:

using Ravencoin.ApplicationCore.Models;
using System.Threading.Tasks;

namespace Ravencoin.ApplicationCore.BusinessLogic
{
    public class Blockchain
    {
        /// <summary>
        /// Returns an object containing various state info regarding blockchain processing.
        /// </summary>
        /// <param name="connection">ServerConnection (required)</param>
        /// <returns>A Json response with the current state of the blockchain</returns>
        public static async Task<ServerResponse> GetBlockchainInfo(ServerConnection connection)
        {
            ServerCommand request = new ServerCommand()
            {
                commandId = "0",
                commandMethod = "getblockchaininfo",
                commandJsonRpc = "2.0"
            };

            ServerResponse response = await RpcConnections.RavenCore.Connect(request, connection);

            return response;
        }

        /// <summary>
        /// Returns the number of blocks in the longest blockchain.
        /// </summary>
        /// <param name="connection">ServerConnection (required)</param>
        /// <returns>
        /// Result:
        /// n(numeric) The current block count
        /// </returns>
        public static async Task<ServerResponse> GetBlockCount(ServerConnection connection)
        {
            ServerCommand request = new ServerCommand()
            {
                commandId = "0",
                commandMethod = "getblockcount",
                commandJsonRpc = "2.0"
            };

            ServerResponse response = await RpcConnections.RavenCore.Connect(request, connection);

            return response;
        }
    }
}


Notice there’s a lot more comments above the functions. if you type /// <enter> in visual studio above your functions, it will autogenerate this comment style. This also will be displayed by intellisense when we’re calling it later, similar to this:

Lets do our Assets logic. Right click the BusinessLogic folder, Go to Add -> Class. Name it Assets.cs.

Add this code to Assets.cs:

using Ravencoin.ApplicationCore.Models;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace Ravencoin.ApplicationCore.BusinessLogic
{
    public class Assets
    {
        /// <summary>
        /// Gets data about a particular asset. 
        /// </summary>
        /// <param name="assetName"> asset_name (string, required)</param>
        /// <param name="connection"> ServerConnection (required) </param>
        /// <returns>
        /// Result:
        ///{
        ///  name: (string),
        ///  amount: (number),
        ///  units: (number),
        ///  reissuable: (number),
        ///  has_ipfs: (number),
        ///  ipfs_hash: (hash), (only if has_ipfs = 1 and that data is a ipfs hash)
        ///  txid_hash: (hash), (only if has_ipfs = 1 and that data is a txid hash)
        ///  verifier_string: (string)
        /// }
        /// </returns>
        /// 
        public static async Task<ServerResponse> GetAssetData(string assetName, ServerConnection connection)
        {

            //Wrap properties in a JObject
            JObject commandParams = new JObject();
            commandParams.Add("asset_name", assetName);
            

            //Set up the ServerCommand
            ServerCommand request = new ServerCommand()
            {
                commandId = "0",
                commandMethod = "getassetdata",
                commandParams = commandParams,
                commandJsonRpc = "2.0"
            };

            //Send the ServerCommand to RavenCore. See comments for Response Value
            ServerResponse response = await RpcConnections.RavenCore.Connect(request, connection);

            return response;
        }
    }
}

Lets do our Transactions logic. Right click the BusinessLogic folder, Go to Add -> Class. Name it Transactions.cs.

Add this code to Transactions.cs:

using Newtonsoft.Json.Linq;
using Ravencoin.ApplicationCore.Models;
using System.Threading.Tasks;

namespace Ravencoin.ApplicationCore.BusinessLogic
{
     public class Transactions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="txid"></param>
        /// <param name="connection"></param>
        /// <returns>"data"      (string) The serialized, hex-encoded data for 'txid'</returns>
        public static async Task<ServerResponse> GetRawTransaction(string txid, ServerConnection connection)
        {
            //Set up parameters to get the hex string of the transaction
            JObject commandParams = new JObject(
                new JProperty("txid", txid)
            );
            //Set up the Ravcencoin Object
            ServerCommand request = new ServerCommand()
            {
                commandId = "0",
                commandMethod = "getrawtransaction",
                commandParams = commandParams,
                commandJsonRpc = "2.0"
            };

            //Get the hex string of the transaction back from getrawtransaction, and then parse it to get just the raw hex string from result
            ServerResponse response = await RpcConnections.RavenCore.Connect(request, connection);

            //Parse the result for the hexstring
            JObject result = JObject.Parse(response.responseContent);
            JToken hexstring = result["result"];

            response.responseContent = hexstring.ToString();

            return response;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hexstring"></param>
        /// <param name="connection"></param>
        /// <returns>
        /// {
        ///  "txid" : "id",        (string) The transaction id
        ///  "hash" : "id",        (string) The transaction hash(differs from txid for witness transactions)
        ///  "size" : n,             (numeric) The transaction size
        ///  "vsize" : n,            (numeric) The virtual transaction size(differs from size for witness transactions)
        ///  "version" : n,          (numeric) The version
        ///  "locktime" : ttt,       (numeric) The lock time
        ///  "vin" : [               (array of json objects)
        ///     {
        ///       "txid": "id",    (string) The transaction id
        ///       "vout": n,         (numeric) The output number
        ///       "scriptSig": {     (json object) The script
        ///         "asm": "asm",  (string) asm
        ///         "hex": "hex"   (string) hex
        ///       },
        ///       "txinwitness": ["hex", ...] (array of string) hex-encoded witness data(if any)
        ///       "sequence": n(numeric) The script sequence number
        ///     }
        ///     ,...
        ///  ],
        ///  "vout" : [             (array of json objects)
        ///     {
        ///       "value" : x.xxx,            (numeric) The value in RVN
        ///       "n" : n,                    (numeric) index
        ///       "scriptPubKey" : {          (json object)
        ///         "asm" : "asm",          (string) the asm
        ///         "hex" : "hex",          (string) the hex
        ///         "reqSigs" : n,            (numeric) The required sigs
        ///         "type" : "pubkeyhash",  (string) The type, eg 'pubkeyhash'
        ///         "asset" : {               (json object) optional
        ///           "name" : "name",      (string) the asset name
        ///           "amount" : n,           (numeric) the amount of asset that was sent
        ///           "message" : "message", (string optional) the message if one was sent
        ///           "expire_time" : n,      (numeric optional) the message epoch expiration time if one was set
        ///         "addresses" : [           (json array of string)
        ///           "12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc"   (string) raven address
        ///           ,...
        ///         ]
        ///       }
        ///     }
        ///     ,...
        ///  ],
        ///}
        /// </returns>
        public static async Task<ServerResponse> DecodeRawTransaction(string hexstring, ServerConnection connection)
        {
            //Set up parameters to get the hex string of the transaction
            JObject commandParams = new JObject(
                new JProperty("hexstring", hexstring)
            );
            //Set up the Ravcencoin Object
            ServerCommand request = new ServerCommand()
            {
                commandId = "0",
                commandMethod = "decoderawtransaction",
                commandParams = commandParams,
                commandJsonRpc = "2.0"
            };

            //Get the hex string of the transaction back from getrawtransaction, and then parse it to get just the raw hex string from result
            ServerResponse response = await RpcConnections.RavenCore.Connect(request, connection);

            return response;
        }
        /// <summary>
        /// Looks up the raw transaction and then decodes the raw transaction to return a verbose json response about the transactionid
        /// </summary>
        /// <param name="txid"></param>
        /// <param name="connection"></param>
        /// <returns>See return details from "DecodeRawTransaction"</returns>
        public static async Task<ServerResponse> GetPublicTransaction(string txid, ServerConnection connection)
        {
            //Get the RawTransaction and return the hexcode
            ServerResponse hexcode = await GetRawTransaction(txid, connection);

            //Get Full Transaction from Hexcode
            ServerResponse response = await DecodeRawTransaction(hexcode.responseContent, connection);

            return response;
        }
    }
}

You can see this code is very similar to what we did last time – but this time instead of making code that gets the hexdata from the txid and decodes it in the same function, we have 3 functions:

  • GetRawTransaction (Returns the hexcode of the transaction from the txid)
  • DecodeRawTransaction (Returns the transaction details from the hexcode)
  • GetPublicTransaction (Calls GetRawTransaction and then DecodeRawTransaction and returns the transaction details)

Why do is this way? Well, we may want to use GetRawTransaction and DecodeRawTransaction on their own for something else, so why write it twice?

This is all we have to do for now inside Ravencoin.ApplicationCore. Hopefully you have no errors right now.


Hooking up Ravencoin.Web to use Ravencoin.ApplicationCore

OK, so we made these models, functions etc in Ravencoin.ApplicationCore. We now need to add a dependency in Ravencore.Web to be able to use them.

Right click on the “Dependencies” item under Ravencore.Web. Choose “Add Project Reference”.

Choose Ravencoin.ApplicationCore by clicking the checkbox, and then hit OK.


Adding Server Configuration and Logging

Remember how we had our connection details hard coded into the class that called the RPC Server? That’s a thing of the past now. We’re going to store it in appsetting.json in the root of your Ravencoin.Web project. We’ll then use Dependency injection into each of our controllers to read this info.

Your appsettings.json should look like this (but with your own connection details of course):

{
  "ServerConnection": {
    "host": "127.0.0.1",
    "port": "8766",
    "username": "cryptobullsh",
    "password": "test12311"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Now, in your Startup.cs we’re going to add a line that tells our application that this section of the config gets mapped to a ServerConnection Object. On lines 17 and 21 You can see we’re using configuring the service to reference the appsettings.json and Configure it.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ravencoin.ApplicationCore.Models;

namespace Ravencoin.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
      
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<ServerConnection>(Configuration.GetSection("ServerConnection"));
            services.AddControllers();
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        { 
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });  
        }
    }
}

Now, lets open up Program.cs in the root of Ravencoin.Web. We’re going to add the logging provider into here.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;


namespace Ravencoin.Web
{
    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>();
                })
                .ConfigureLogging(logging =>
                {
                    // clear default logging providers
                    logging.ClearProviders();

                    // add built-in providers manually, as needed 
                    logging.AddConsole();
                    logging.AddDebug();
                    logging.AddEventLog();
                    logging.AddEventSourceLogger();
                });
    }
}

A little more housecleaning…

We have some files that are useless or will be recreated with different names. Delete these files:

  • Ravencore.cs
  • RavencoinRequest.cs
  • /Controllers/GetAssetDataController.cs
  • /Controllers/GetBlockchainInfoController.cs
  • /Controllers/GetBlockchainInfoController.cs

Cool. We should be good to start implementing the simplified web code into some new controllers now.


Creating new Controllers

One of the design principles I wanted was to combine operations into a single controller. If its a Blockchain request it would go to the blockchain controller, if its an asset request, it goes to the assets controller and so on. We can have multiple actions within one controller.

Lets start with Blockchain. Right click Controllers and go to Add -> Class. Call it BlockchainController.cs.

using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Ravencoin.ApplicationCore.BusinessLogic;
using Ravencoin.ApplicationCore.Models;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using System;

namespace Ravencoin.Web.Controllers
{
    [ApiController]
    [Route("/api/Blockchain/[action]")]
    public class BlockchainController : Controller
    {
        //Inject Server Configuration
        private readonly ILogger logger;
        private readonly IOptions<ServerConnection> serverConnection;
        public BlockchainController(IOptions<ServerConnection> serverConnection, ILogger<AssetsController> logger)
        {
            this.serverConnection = serverConnection;
            this.logger = logger;
        }

        public async Task<ServerResponse> GetBlockchainInfo(){
            logger.LogInformation($"Getting Blockchain Info");
            try{
                ServerResponse response = await Blockchain.GetBlockchainInfo(serverConnection.Value);
                return response;
            }
            catch (Exception ex){
                logger.LogError($"Exception: {ex.Message}");
                ServerResponse errResponse = new ServerResponse(){
                    statusCode = System.Net.HttpStatusCode.InternalServerError,
                    errorEx = ex.Message
                };
                return errResponse;
            }
        }

        public async Task<ServerResponse> GetBlockCount(){
            logger.LogInformation($"Getting latest block count");
            try{
                ServerResponse response = await Blockchain.GetBlockCount(serverConnection.Value);
                return response;
            }
            catch (Exception ex){
                logger.LogError($"Exception: {ex.Message}");
                ServerResponse errResponse = new ServerResponse(){
                    statusCode = System.Net.HttpStatusCode.InternalServerError,
                    errorEx = ex.Message
                };
                return errResponse;
            }
        }
    }
}

Lets start with what’s changed with the [Route] up on top. I’ve changed it to use /api/Blockchain/[action] instead of [controller]. This allows us to use either https://<host>/api/Blockchain/GetBlockchainInfo and https://<host>/api/Blockchain/GetBlockCount as different calls in the same URL structure even though those functions are in the same controller.

Up next in lines 16-22, we’re using dependency injection to bring the ServerConnection and the Logging objects we created earlier.

If we didn’t have all the logging and the try/catch blocks, the only thing we need to get the blockchain info is a single line now – ServerResponse response = await Blockchain.GetBlockchainInfo(serverConnection.Value);

Much simpler right?


Writing the other controllers

Right click the Controllers Folder, and click Add -> New Class. Name it Assets.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Ravencoin.ApplicationCore.Models;
using Ravencoin.ApplicationCore.BusinessLogic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using System;

namespace Ravencoin.Web.Controllers
{
    [ApiController]
    [Route("/api/Assets/[action]")]
    public class AssetsController : Controller
    {
        private readonly ILogger logger;

        //Inject Server Configuration
        private readonly IOptions<ServerConnection> serverConnection;
        public AssetsController(IOptions<ServerConnection> serverConnection, ILogger<AssetsController> logger)
        {
            this.serverConnection = serverConnection;
            this.logger = logger;
        }

        public async Task<ServerResponse> GetAssetData(string asset){
            logger.LogInformation($"Getting Asset data for {asset}");
            try{
                ServerResponse response = await Assets.GetAssetData(asset, serverConnection.Value);
                return response;
            } catch (Exception ex){
                logger.LogError($"Exception: {ex.Message}");
                ServerResponse errResponse = new ServerResponse(){
                    statusCode = System.Net.HttpStatusCode.InternalServerError,
                    errorEx = ex.Message
                };
                return errResponse;
            }
        }
    }
}

Right click the Controllers Folder, and click Add -> New Class. Name it Transactions.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Ravencoin.ApplicationCore.Models;
using Ravencoin.ApplicationCore.BusinessLogic;
using System.Threading.Tasks;
using System;

namespace Ravencoin.Web.Controllers
{
    [ApiController]
    [Route("api/Transactions/[action]")]
    public class TransactionController : ControllerBase
    {
        private readonly ILogger logger;

        //Inject Server Configuration
        private readonly IOptions<ServerConnection> serverConnection;
        public TransactionController(IOptions<ServerConnection> serverConnection, ILogger<TransactionController> logger){
            this.serverConnection = serverConnection;
            this.logger = logger;
        }

        [HttpGet]
        public async Task<ServerResponse> GetPublicTransaction(string txid)
        {
            logger.LogInformation($"Getting Transaction data for {txid}");
            try{
                ServerResponse response = await Transactions.GetPublicTransaction(txid, serverConnection.Value);
                return response;
            }
            catch (Exception ex){
                logger.LogError($"Exception: {ex.Message}");
                ServerResponse errResponse = new ServerResponse(){
                    statusCode = System.Net.HttpStatusCode.InternalServerError,
                    errorEx = ex.Message
                };
                return errResponse;
            }
        }
    }
}

Testing it out

OK lets test out what we did. Clock the little play icon to start the application.

Blockchain Operations

Lets head over to https://<host>/api/Blockchain/GetBlockchainInfo:

And now to https://<host>/api/Blockchain/GetBlockCount:

Asset Operations

Head over to https://<host>/api/Assets/GetAssetData?asset=TRASH

Transaction Operations

Head over to https://<host>/api/Transactions/GetPublicTransaction?txid=a16923c2dd1ed12f2c022437fe54a4d5222ed0f8dacecc9223c69efd27d32c61

Cool, everything works.


A note about errors

Notice we’re also not just returning the json from the Ravencore RPC server now. We’re returning one of our own models we made earlier, called ServerResponse, where we include data like the status code, if there is an error, and of course the content itself. We could later do things like add a property about how long the request took if we wanted, etc.

As an example of an error, if i put in the wrong password for the rpc server in my config file, we’ll see this:

Just to note, all errors coming from Ravencore.cs will be 400. This is due to Microsoft not exposing statusCode in the HttpRequestException class. It’s on my //Todo’s to figure out a better way to handle different exceptions – but it’s a little pointless since the Ravencore RPC server doesn’t really give us any meaningful errors anyway. Most errors are simply “Response status code does not indicate success: 500 (Internal Server Error).”.


Did I help? If you’re feeling generous, buy me a coffee by sending RVN to RHEH92NguBjaxXQPsM1bedkqqTXKr9EZcM

Follow me on social media:

0 thoughts on “Intro to Ravencoin Development – Part 2 – Refactoring

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>