0 Comments

If you are using your own domain to send emails, you may get the suspicion that your mails may land in the spam folder of your recipients.

MxToolBox is an easy way to check if your domain (or the IP of your mail server) is listed on a spam list.

With MailRadar you can check if you configured your mail server correctly: not allowing others to use it as a relay server.

0 Comments

 

Auch dieses Jahr war ich wieder bei der Spartakiade in Berlin. Das ist eine Wochenend-Konferenz die aus Workshops besteht. Jeden Tag gibt es ca. 10 verschiedene Workshops.

Am Samstag war ich bei einer Einführung in Angular 4 (Ferdinand Malcher und Johannes Hoppe). Ich hatte letztes Jahr an einem “funky friday” schon mal eine kleine Beispielanwendung mit Angular 2 gebaut. Damals war ich ziemlich erschlagen von der Komplexität der Komponenten (TypeScript, Transpilierung, Konfigurationen), die alle zusammenspielen, um eine Single-Page-Anwendung mit Angular zu bauen. Aus dem Workshop ging ich aber mit einem Gefühl raus die Komplexität deutlich besser verstanden zu haben. Ich würde aber weiterhin Angular nur dann empfehlen, wenn man eine neue, nicht gerade kleine Webpage bauen möchte. Man muss sich in die Struktur reindenken – da macht es keinen Sinn, wenn man nur ab und zu etwas an dem Projekt macht.

Spartakiade 2017 Angular 4

Am Sonntag war ich beim Workshop “.NET in the box” von Frank Pommerening. Ich habe mich da zum ersten Mal ausführlich mit dem Thema Container beschäftigt. Wir probierten mit Docker das Erstellen und Starten von Containern aus. Ich sehe da noch keine sinnvolle Anwendung für unsere Firma, aber eine interessante Technologie.

Insgesamt hat es mir das Wochenende gut gefallen. Wir haben in den Workshops viel an unseren Rechnern programmiert und konfiguriert – das hat Spaß gemacht. Der Service rundum war wieder toll: leckeres, vielfältiges Essen und Trinken, nette Leute :).

Spartakiade 2017 Mittag

0 Comments

Our goal was to append a row to an existing Google Spreadsheet. That spreadsheet is not publicly shared.

The standard scenario seems to be that a user who has access rights interacts with your program and authorizes it to make changes to the spreadsheet. That is not what we needed. In our case an arbitrary user interacts with the website and the server then sends some data to the spreadsheet. The spreadsheet owner is the portal owner, not the interacting user.

There is an easy way to accomplish that: via OAuth for Service Accounts. There is also a Google API client library in .NET for simple communication.

Here is an overview over the steps:

  1. Go to Google APIs and create an API project. Enable Sheets API.
  2. Create Credentials: create Service account key (AppEngine Default Service Account) and save the JSON file with the credentials in your project
  3. Share spreadsheet with Google-API-User, that is the client_email in the downloaded credential JSON file.
  4. Write your code and use the downloaded credentials:

Here is our example code:

using System;
using System.Collections.Generic;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;

namespace SheetApiTest
{
public class AppendWithGoogleCredentials
{
static string[] Scopes = { SheetsService.Scope.Spreadsheets };
static string ApplicationName = "Google Sheets API .NET Quickstart";

public void AppendData()
{
// downloaded json file with private key
var credential = GoogleCredential.FromStream(new FileStream("sheets-test.json", FileMode.Open)).CreateScoped(Scopes);

// Create Google Sheets API service.
var service = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});

var spreadsheetId = "11AwV7d1pEPq4x-rx9WeZHNwGJa0ehrCSKyWyfRhh760";
// data to append - must be a value range:
var valueRange = new ValueRange { Values = new List<IList<object>> { new List<object>() } };

// add data for each column
valueRange.Values[0].Add(DateTime.Now.ToLongTimeString());
valueRange.Values[0].Add("b");
valueRange.Values[0].Add("c");

// in append request, the range only requires the name of the table
var rangeToWrite = "Sheet1";
// append request: into first free line
var appendRequest = service.Spreadsheets.Values.Append(valueRange, spreadsheetId, rangeToWrite);
appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
var appendReponse = appendRequest.Execute();
}
}
}

Have success moving your data!

Anton.

0 Comments

Das Event

Die Spartakiade fand zum 5 Mal statt (ich war zum vierten Mal dabei, glaube ich). Sie wurde toll organisiert: Es gab genug und gut zu essen und trinken, das WLAN funktioniert einwandfrei – damit waren die Grundbedürfnisse von Entwicklern gedeckt :). Wir waren wieder bei Immobilien Scout untergebracht. Die Spartakiade war schnell ausgebucht und sehr gut besucht. Die Stimmung war engagiert, die Leute waren gut drauf. Es macht Spaß an dem Event teilzunehmen.

Der Workshop

Ich habe mir Asynchrone Programmierung bei Daniel Marbach ausgesucht, da ich in diesem Thema ein Novize bin und es an vielen Stellen Einzug hält, auch (oder insbesondere) bei Webanwendungen. Daniel argumentierte, dass asynchroner Code in Zukunft überall Einzug halten wird, fast jede Library wird mittlerweile asynchron geschrieben. Hier findet man den Quellcode zum Workshop: https://github.com/danielmarbach/async-dolls

In .NET arbeitet man mit der TPL (Task Parallel Library). Die Bausteine - die Tasks - werden durch einen Task Scheduler koordiniert. Ein Task kann unterschiedliche Operationen representieren: IO-bound oder CPU-bound operations. CPU-bound Tasks blockieren worker (je nach CPU-Chip hat man davon mehr oder weniger), während IO-bound Tasks concurrent (nebenläufig) arbeiten können. Wir haben uns im Workshop fast nur mit IO-bound Tasks auseinandergesetzt. In C#  werden die Keywords async und await als syntaktischer Zucker bereitgestellt, damit man automagisch mit Zustandsmaschinen arbeiten kann.

Hier ein paar lose weitere Erkenntnisse / Best Practices:

  • IO-bound kann Netzwerkkommunikation sein (z.B. TCP), Datenbankabfragen, Filesystem-Anfragen (wobei File-Zugriffe in .NET fast alle synchron programmiert sind).
  • sequential: await tasks, concurrent: await Task.WhenAll(tasks)
  • Nie async void verwenden, sondern immer Task zurückgeben (sonst bekommt man die Exceptions des inneren Codes nicht zu Gesicht).
  • In Backend und Libraries ConfigureAwait(false) verwenden, damit der Context nicht zwischengespeichert werden muss.
  • Wenn man synchrone APIs in asynchronem Codepfad aufruft danach leeren Task erstellen:
    • return Task.FromResult(0) oder return Task.CompletedTask() (ab .NET Framework 4.6)
  • Task.Run().GetAwaiter().GetResult() entpackt Exceptions (sonst sind sie gewrappt in AggregateException)
  • besser Task.Run nutzen statt Task.Factory.StartNew, da es dann keines Unwraps bedarf
  • Semaphore (Limitierung der concurrency): in .NET SemaphoreSlim verwenden (asynchron implementiert)
  • async / await ist nicht threadsicher (könnte von einem Thread angefangen werden und von anderem beendet werden)
    • Daher nicht ThreadLocal verwenden, sondern AsyncLocal, falls Zustand gespeichert werden muss.
    • Im IoC nicht “per Thread” nutzen.
  • Man kann Fire and Forget schreiben, wenn man den Task nicht awaiten möchte: Fire().Ignore() wobei Ignore() einfach eine leere Task-Methode ist.

Wir programmierten in dem zweitägigen Workshop eine Message-Pump it einer Pipeline. Nach und nach implementierten wir die Einzelteile und gelangten zu tieferem Verständnis der asynchronen Programmierung. Hier findet man den Quellcode zum Workshop: https://github.com/danielmarbach/async-dolls. Es war ein fordernder Workshop, aber ich habe viele Erkentnisse gewonnen, die ich hoffetnlich lange genug im Kopf behalte, bis ich das nächste Mal mit asynchronen Methoden zu tun habe.

0 Comments

We had the problem that Microsoft servers rejected emails from our hMailServer. We got the following replies when we send a mail to @outlook.com or @live.com:

Remote server replied: 550 SC-001 (COL004-MC6F13) Unfortunately, messages from <our IP> weren't sent. Please contact your Internet service provider since part of their network is on our block list. You can also refer your provider to http://mail.live.com/mail/troubleshooting.aspx#errors.

After looking for the error code and possible causes, it seems like spam has been sent via our IP, but none that we know of. It could be that it did not originate from our server, but from a server with an IP address starting with the same digits as ours (a whole range belongs to our server hosting provider).

How did I fix it:

  1. Checked if other services think that spam is being sent via our IP:
    1. https://www.spamhaus.org/lookup/
    2. http://mxtoolbox.com/blacklists.aspx
    3. https://www.senderscore.org/lookup.php?lookup=0
  2. Contact Outlook.com to let emails from our server through: contact form. After replying to their mail, our emails were not rejected anymore after a few days.
  3. Monitor emails going out from our server to Microsoft/Outlook: We signed up for Smart Network Data Service. There we added our IP, signed the contract, and will now be notified of spam and junk sent from our server.

Our emails now seem to land in the junk email folder, but at least they are pass through. We will look into the problem to persuade Microsoft to not mark our emails as junk.