Vova Bilyachat

Melbourne, Australia

How different are frameworks and languages ​​nowadays?

30 October 2020

Where it started?

I did have many conversation with people about which language or technology is better. I did work for many years with c# and past few years I am enjoying java, I wouldn’t mind to do node.js or golang too, and I found that languages and frameworks are quite similar.

One last conversation with one of my friend gave me idea for my new blog post.

So I decided, well instead of arguing lets try and see is it really true that some devs write much less code then others?

PS. In some examples I’ve ommited imports since its usually done by IDE.

Start http server

Node.js / express

require('express')
  .get('/', (req, res) => {
    res.send('Hello from express!');
  })
  .listen(3000, () => {
    console.log(`Example app listening at http://localhost:3000`);
  });

Asp.net Core

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
WebHost
    .Start(endpoints =>
    {
        endpoints.MapGet("/", async context => await context.Response.WriteAsync("Hello world!"));
    });

Spring boot

package com.minimal.minimal;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class MinimalApplication {
    @GetMapping("/")
    public String GetEndpoint() {
        return "Hello from spring";
    }

    public static void main(String[] args) {
        SpringApplication.run(MinimalApplication.class, args);
    }
}

Adding security Web headers to it

Node.js

Install package npm i helmet then in code

const app = require('express');
app.use(require('helmet')());

Asp.net core

I did not find how to do that out of box, so I found package. Install Install-Package NWebsec.AspNetCore.Middleware then in code it would be required to enable each security we want.

    app.UseXXssProtection(options => options.EnabledWithBlockMode());
    app.UseXfo(options => options.SameOrigin());

Spring boot

For spring this is comming from spring security and require 3 dependencies.

 <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>5.4.1</version>
        </dependency>
        <!-- Spring Security Config -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.4.1</version>
        </dependency>

        <!-- Spring Security Web -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>5.4.1</version>
        </dependency>

In code use annotation @EnableWebSecurity.

package com.minimal.minimal.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().permitAll();
    }
}

Note it will also enable authorization :)

Return file in api

Node.js

app.get('/download', function (req, res) {
  res.download('test.pdf');
});

Asp.net core

endpoints.MapGet("/return-file", async context => { 
    await context.Response.SendFileAsync("test.pdf"); 
});

Spring boot

    @GetMapping(value = "/return-file", produces = MediaType.APPLICATION_PDF_VALUE)
    public FileSystemResource getImageWithMediaType() {
        return new FileSystemResource("test.pdf");
    }

Get list of an S3 folder files

Node.js

app.get('/list-s3', async (req, res) => {
  const s3 = new AWS.S3({ apiVersion: '2006-03-01' });
  const files = await s3.listObjects({ Bucket: 'blog-tests' }).promise();
  res.send(files.Contents.map((c) => c.Key));
});

Asp.net core

 endpoints.MapGet("/list-s3", async context => {
    var client = (IAmazonS3) context.RequestServices.GetService(typeof(IAmazonS3));
    var response = await client.ListObjectsAsync(new ListObjectsRequest {BucketName = "blog-tests"});
    await context.Response.WriteAsJsonAsync(response.S3Objects.Select(o => o.Key));
});

Spring boot

@GetMapping(value = "/list-s3", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<Stream<String>> listS3() {
    return Mono.fromFuture(s3client.listObjects(builder -> builder.bucket("blog-tests")))
            .map(r->r.contents().stream().map(c->c.key()));
}

Converting some JSON props from strings to numbers

I think its hard to properly give example since JS is not strogly typed while c# and java is. So even solution would really depend on a problem, but anywhay I tried my best

Node.js

app.get('/convert-json', function (req, res) {
    const jsonString = "{\"Day\": \"1\", \"Month\": \"test\", \"Year\": \"2020\"}";
    res.send(cloneDeepWith(JSON.parse(jsonString),  n => /^[+-]?(\d*\.)?\d+$/.test(n) ? Number(n) : undefined));
});

Asp.net core

So in c# it will a bit more since I need to create type first

  public class TestDate
    {
        public int Day { get; set; }
        public int Month { get; set; }
        public int Year { get; set; }
    }

But then during serialization just allow convertion from string to number.

 endpoints.MapGet("/convert-json", async context => {
    var jsonString = "{\"Day\": \"1\", \"Month\": \"2\", \"Year\": \"2020\"}";
    var json = JsonSerializer.Deserialize<TestDate>(jsonString, new JsonSerializerOptions {
        NumberHandling = JsonNumberHandling.AllowReadingFromString
    });
    await context.Response.WriteAsJsonAsync(json);
});

Spring boot

Spring boot solution would be similar creating a type the only difference is that ObjectMapper will automatically convert json string to object ints

package com.minimal.minimal.controller;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class TestDate
{
    public int day;
    public int month;
    public int year;
}

Then

@GetMapping(value = "/convert-json", produces = MediaType.APPLICATION_JSON_VALUE)
public TestDate convertJson() throws JsonProcessingException {
    String json = "{\"day\": \"1\", \"month\": \"4\", \"year\": \"2020\"}";
    var serializer = new ObjectMapper();
    return serializer.readValue(json, TestDate.class);
}

Start receiving web socket events

Node.js

const server = require('http').createServer(app);
const io = require('socket.io')(server);
io.path('/ws');
io
    .on('connection', (socket) => {
        socket.on('chat', (data) => {
            console.log(data)
        });
    });

Asp.net core

using System;
using System.Text;
using System.Threading;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;

WebHost.StartWith(app =>
{
    app.UseWebSockets();
    app.Use(async (context, next) =>
    {
        if (context.Request.Path == "/ws")
        {
            var webSocket = await context.WebSockets.AcceptWebSocketAsync();
            var buffer = new ArraySegment<byte>(new byte[1024 * 4]);
            var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
            await webSocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hi from the core :) ")),
                result.MessageType, result.EndOfMessage, CancellationToken.None);
        }
        else
        {
            await next();
        }
    }); 
});
Console.ReadKey();

And now lets test

var url = "ws://localhost:5000/ws";
var socket = new WebSocket(url);
socket.onopen = function(e) {
    console.log("connected to " + url)
    socket.send("Hi");
};

.Net core websocket

Spring boot

Include dependencies

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

Include configuration

package com.minimal.minimal.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    private WebSocketHandler handler;

    @Autowired
    public WebSocketConfig(WebSocketHandler handler) {
        this.handler = handler;
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
        webSocketHandlerRegistry.addHandler(handler, "/ws").setAllowedOrigins("*");
    }
}

Handler

package com.minimal.minimal.websocket;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

@Component
public class WebSocketHandler extends TextWebSocketHandler {

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message)
            throws Exception {

        var clientMessage = message.getPayload();

        if (clientMessage.startsWith("Hello")) {
            session.sendMessage(new TextMessage("Hello from spring :)"));
        }
    }
}

Now lets test it

var url = "ws://localhost:5050/ws";
var socket = new WebSocket(url);
socket.onopen = function(e) {
    console.log("connected to " + url)
    socket.send("Hello");
};

Java websocket

Summary

It was really nice idea to try to implement same thing in different languages and I would still stay with my idea that JS, C# and Java is quite similar since all of them are object oriented languages. Also the other fact is that using proper idea helps alot in c# and java we dont care much about imports since its IDEA responsibility. In addition code is part of the solution usually toolling is very important, performance and most important team skills since in the end of the day its develiper who write code :)

On the other hand where I think bigger difference could be if we compare functional and object oriented languages but that could be idea for next topic.