Let’s start with the server part of our appliation first

Diagram

We will create a new .NET Core Web Api project.

First, make sure you have .NET Core installed on your machine. For more details click here

At the time of this writing I use .NET Core version 3.0.100.

Navigate to your projects folder and create a new folder for our code-converter

md code-converter && cd code-converter

Then create a new folder for our server project

md server && cd Server
dotnet new webapi --name Server --output .

To test that our server is running let’s add a new hello endpoint.

First, open the project with your code editor of choice and navigate to the Controllers folder. You can delete the WeatherForecastController.cs file as we won’t need it. Instead, add a new CodeConverter.cs file with the following contents


using Microsoft.AspNetCore.Mvc;
using System.Linq;

namespace Server.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ConvertController : ControllerBase
    {
        [HttpGet]
        [Route("hello")]
        public IActionResult Hello()
        {
            return Ok("Hello World");
        }
    }
}

Go back to your terminal and run your project with

dotnet run

If everything is working as expected, when you navigate to https://localhost:5001/convert/hello you should see Hello World in your browser.

Now it’s time to add the code converter library as a dependency. We will use the .NET cli for that

dotnet add package ICSharpCode.CodeConverter --version 7.2.0

We can now remove the Hello method in our controller and add a new Convert method that will do the converion for us.

Before adding the Convert post method, let’s first create the ConvertRequest model that we will use for conversion arguments.

Create a new top-level folder named Models and add a new ConvertRequest.cs file with the following contents

namespace Server.Models
{
    public class ConvertRequest
    {
        public string From { get; set; }
        public string To { get; set; }
        public string Code { get; set; }
    }
}

Our ConvertRequest has just From language and To language. We will add more options later on.

Now that we have the ConvertRequest we can add the ConvertPost method to our ConvertController.cs like this

[HttpPost]
public async Task<IActionResult> Convert([FromBody] ConvertRequest request)
{
    var codeWithOptions = new CodeWithOptions(request.Code)
        .WithTypeReferences(GetDefaultReferences())
        .SetFromLanguage(request.From, GetDefaultLanguageVersion(request.From))
        .SetToLanguage(request.To, GetDefaultLanguageVersion(request.To));
    var result = await ICSharpCode.CodeConverter.CodeConverter.Convert(codeWithOptions);

    return Ok(result);
}

private IReadOnlyCollection<PortableExecutableReference> GetDefaultReferences()
{
    return new PortableExecutableReference[] { };
}

private int GetDefaultLanguageVersion(string language)
{
    switch (language)
    {
        case "C#":
            return 6;
        case "Visual Basic":
            return 14;
        default:
            throw new ArgumentException(language);
    }
}

Maybe you’ve noticed that we prepare CodeWithOptions object that has a References property currently being an empty collection. The conversion process needs to prepare a roslyn project at runtime where we can add our source code as a document. For now, let’s just add a few references and we will see at a later stage how the references impact the conversion, like this

private IReadOnlyCollection<PortableExecutableReference> GetDefaultReferences()
{
    Type[] types  = {
        typeof(System.Text.Encoding),
        typeof(System.Linq.Enumerable),
        typeof(System.Dynamic.DynamicObject),
        typeof(System.Net.Http.HttpClient),
        typeof(Microsoft.VisualBasic.Constants)
    };
    return types.Select(type => MetadataReference.CreateFromFile(type.Assembly.Location))
        .ToList()
        .AsReadOnly();
}

The Code Converter library works only for C# 6 and Visual Basic 14, so it doesn’t make sense to expose the versions as part of the API, but it would be better if we validate From and To to be only C# or Visual Basic, like this

private const string CSharp = "C#";
private const string VisualBasic = "Visual Basic";

[HttpPost]
public async Task<IActionResult> Convert([FromBody] ConvertRequest request)
{
    if (request.From != CSharp && request.From != VisualBasic)
    {
        return BadRequest(string.Format("{0} argument can be either {1} or {2}", "'From'", CSharp, VisualBasic));
    }
    else if (request.To != CSharp && request.To != VisualBasic)
    {
        return BadRequest(string.Format("{0} argument can be either {1} or {2}", "'To'", CSharp, VisualBasic));
    }
    else if (string.IsNullOrEmpty(request.Code))
    {
        return BadRequest("Please provide a code to convert");
    }

    var codeWithOptions = new CodeWithOptions(request.Code)
        .WithTypeReferences(GetDefaultReferences())
        .SetFromLanguage(request.From, GetDefaultLanguageVersion(request.From))
        .SetToLanguage(request.To, GetDefaultLanguageVersion(request.To));
    var result = await ICSharpCode.CodeConverter.CodeConverter.Convert(codeWithOptions);

    return Ok(result);
}

Not it’s time to test our conversion service, back in the terminal and run

dotnet run

Your server should be running at https://localhost:5001 and we can initiate a few post requests to test if conversion works as expected.

My favourite tool for testing web apis is Postman. When you launch it, make sure that you have Settings -> General -> SSL certificate verification is Off.

Then we create a new Post request to https://localhost:5001/convert, then go to Body tab, change the type to raw and choose JSON from the dropdown. Then enter the arguments we want to send in the body, like this

{
    "From": "C#",
    "To": "Visual Basic",
    "Code": "namespace Test {}"
}

Body

When you send the response you will receive a response with the converted code

Response

And that’s it. In the next post we will continue with the Angular client for our code converter application.