Weekly Notes 2021-W9

Sam BosellSam Bosell
6 min read

These logs are my work logs (anonymized) mixed in with a few thoughts and maybe some personal anecdotes. It is sometimes hard to gauge one's productivity but once you log what you are working on, even if broad, you find that you did work on many things. Today's theme is going to be eliminating distractions. Working from home is awesome and has its own challenges and benefits. I love being able to see my wife and kid but also having to be pulled away from software development can have a devastating impact on productivity. I find that simple solutions like closing the door, putting on headphones and setting an "interruption" schedule helps. You have to find what works for your situation.

Work Logs

Tons of monthly billing reports and reconciliations. Not the most fun thing in the world but necessary to pay the bills.

Looking at helping a client with a large Wordpress Migration as their webhost was complaining about their site being "heavy". I usually stay away from Wordpress but wanted to see if I could help them out. Conclusion: Did not work out, the infrastructure I manage is not Wordpress optimized. I did some analysis on their site and their homepage had a 25+mb payload mostly due to unoptimized images, one of them was 15+mb. Reducing those to normal sizes and using a CDN would probably reduce their server load by 75%.

Various client meetings during the week. A few new projects approved. Staying busy.

Worked on adding some new functionality to an Energy sector web application. Decided to incorporate TailwindCSS into the design of the Vue component and it ended up looking really good. Only slight snag was working out how to deal with some of the Bootstrap conflicts but it wasn't terribly difficult.

Electronic gift card integration into a Square Space eComerce site. Fun little project to be able to get to build out the integration pieces with their webhooks and API.

In a conversation this week someone reminded me of my particular position in a company that I have and although I did not require to be reminded as it wasn't lost on me it really made me pause for a second. Sometimes you need to be reminded even if you know something just to reinforce it and expand your thought process out. I appreciate these conversations more as I think about them.

Still waiting on Umbraco to address this critical issue.

Still working with Microsoft Azure support on some issues in our accounts. This seems like a never ending story.

Coding and Personal Stuff

One of the out of the box functional items I really love about Service Stack is its integration with server side validations. More often than not a developer will add client side validation first which is okay but much less important than server side. A front end developer might disagree but data integrity is generally more important than a pretty UI especially when dealing with a business line application. You can launch a first version with the latter and not the former. The underlying Service Stack validation library supports all the standard validations (required, ranges, dates, messaging/etc) but where it is interesting is creating your own validation method for a particular input. Since most public forms require some kind of "human input validation" like ReCaptcha, I implemented this in more projects than I can count. Let's see how it works.

First, Service Stack supports some convention based Request and Response automatic validations. If you have a request object (what is submitted to the server) with name "FormCreate" and your response is "FormCreateResponse" with a ResponseStatus property it will automatically call any validators for you without having to do anything more than define the validator. Here is a simple example:

public class FormCreate : IReturn<FormCreateResponse> {
  public string Name {get;set;}
  public string Comment {get;set;}
  public string RecaptchaToken {get;set;}
}

public class FormCreateResponse {
  public ResponseStatus ResponseStatus {get;set;}
  public int Id {get;set;}  // maybe the id from the db
}

That just sets up the API interface of receiving a FormCreate and returning a FormCreateResponse. Let's create a simple validator without the Recaptcha part by validating the name and comment with a few simple built in rules:

public class FormCreateValidator : AbstractValidator<FormCreate> {
    public FormCreateValidator() {
        RuleFor(r=>r.Name).NotEmpty().WithMessage("Your name is requird");
        RuleFor(r=>r.Comment).NotEmpty().Length(20, 1000);
    }
}

Now we have out of the box validations anytime a FormCreate is submitted to our services. It will return the appropriate validation messaging back to the client with a 400 error code. In a future post I will show you how to integrate this into a Typescript/Vue front end with the Service Stack client libraries to easily show these error messages.

Weekly Notes 2021-W9

Now on to the fun part. Let's create our own validation for the recaptcha token. Google's Recaptcha service will generate a token on the front end and that will need to be submitted to the server for validation via their manual workflow. For today's post let's just assume the token is submitted via the RecpathaToken property on FormCreate. Next week we can review that part. How do we validate it?

First, I have a utility method that I wrote to do the validation. It calls the verify method from Google and returns the success and score which we will use in the validator. This could be combined with the validator but not all my projects have a Service Stack architecture so it is good to keep this separate.

  public class Recaptcha
    {
        public GoogleRecaptchaConfig Config {get;set;}
        public static String ENDPOINT = "https://www.google.com/recaptcha/api/siteverify";
        public  (bool passed, float score) Verify(string response)
        {
             (bool passed, float score) result = (false,0);

            var json = Recaptcha.ENDPOINT.AddQueryParam("secret", Config.Secret).AddQueryParam("response", response).PostJsonToUrl(null);

            var objResult = JsonObject.Parse(json);
            result.passed = objResult.Get<bool>("success");
            result.score = objResult.Get<float>("score");
            return result;
        }

    }

Now, let's combine our utility method with the custom validator which just implements a couple methods to verify the response and then use our custom rule to determine what we want to pass or not ( greater than .3 in my case).

public interface IRecaptchaValidator
{
    bool ValidResponse(string response);
    (bool passed, float score) VerifyCaptchaResponse(string response);

}

public class RecaptchaValidator : IRecaptchaValidator
{
    public Recaptcha Recaptcha { get; set; }
    public RecaptchaValidator()
    {
    }

    public bool ValidResponse(string response)
    {
        var reResponse = VerifyCaptchaResponse(response);
        return reResponse.passed && reResponse.score > .3;
    }
    public (bool passed, float score) VerifyCaptchaResponse(string response)
    {
        return Recaptcha.Verify(response);
    }
}

Last, we just add this to our FormCreate validation class:

public class FormCreateValidator : AbstractValidator<FormCreate> {
    public IRecaptchaValidator RecaptchaValidator { get; set; }
    public FormCreateValidator() {
        RuleFor(r=>r.Name).NotEmpty().WithMessage("Your name is requird");
        RuleFor(r=>r.Comment).NotEmpty().Length(20, 1000);

    RuleFor(r=>r.RecapchaResponse).Must(r=> RecaptchaValidator.ValidResponse(r)).WithMessage("You didn't pass the human test");  

    }
}

Last point is that to tie all this together the IOC needs to be setup so everything resolves. In Service Stack that's done in the AppHost and follows the same pattern as most other libraries:

 var recaptchaConfig = new GoogleRecaptchaConfig()
 {
   Secret = "",
   SiteKey = ""
 };

// The default for these will be a container based registration as opposed to per request which isn't needed for validators.        
container.Register(recaptchaConfig);
container.RegisterAutoWired<Recaptcha>();
container.RegisterAutoWiredAs<RecaptchaValidator,IRecaptchaValidator>();

And now any class that has an associated validation class and returns the correct response will AUTOMATICALLY be validated on the server for all requests. Next, I'll delve into how to integrate the server side validation responses into a web front end. HINT: Very easy and Service Stack provides the tooling to do it.

Last, I also added the plumbing for a SignalR demo to my personal site and will tweak that next week and started on the next demo which will be an uptime monitor. More to come on this.

0
Subscribe to my newsletter

Read articles from Sam Bosell directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sam Bosell
Sam Bosell