Melbourne, Australia
30 October 2020
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.
Here is the list from the top of my head.
— Vasyl Boroviak (@kore_sar) August 30, 2020
* starting http server
* adding security Web headers to it
* responding with a file
* getting list of an S3 folder files
* converting some JSON props from strings to numbers
* start receiving web socket events
All the above are 1 LOC.
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.
require('express')
.get('/', (req, res) => {
res.send('Hello from express!');
})
.listen(3000, () => {
console.log(`Example app listening at http://localhost:3000`);
});
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
WebHost
.Start(endpoints =>
{
endpoints.MapGet("/", async context => await context.Response.WriteAsync("Hello world!"));
});
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);
}
}
Install package npm i helmet
then in code
const app = require('express');
app.use(require('helmet')());
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());
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 :)
app.get('/download', function (req, res) {
res.download('test.pdf');
});
endpoints.MapGet("/return-file", async context => {
await context.Response.SendFileAsync("test.pdf");
});
@GetMapping(value = "/return-file", produces = MediaType.APPLICATION_PDF_VALUE)
public FileSystemResource getImageWithMediaType() {
return new FileSystemResource("test.pdf");
}
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));
});
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));
});
@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()));
}
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
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));
});
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 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);
}
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)
});
});
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");
};
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");
};
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.