Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Monday, June 17, 2013

What's The Best Structure for API Responses?

I learned something new while listening to Episode 12 of the Traffic and Weather podcast.

John Sheehan mentioned that when you return your XML or JSON from an API you should have an error envelop separate from the data envelop.

The main reason is if you're deserializing the output and mapping it to a class in a static language you can use error.message or error.code without namespace conflicts. This also gives you a consistent target for a given endpoint. John recommends avoiding returning one structure for success and another structure for error (something that I've been doing to date).

My take away is that John recommends structuring your return data like so:

Success:
{
  data: {
    id: 1,
    model: 'Mustang',
    make: 'Ford'
  },
  error: null
}

Error:
{
  error: {
    code: 3,
    message: 'Car not found'
  }
}

Continue reading to see the NOT RECOMMENDED way

The way I traditionally did it was without envelopes but the structure changes based on success vs error:

Success (the car object):
{
  id: 1,
  model: 'Mustang',
  make: 'Ford'
}
Error (the error object):
{
  code: 3,
  message: 'Car not found'
}

What I liked about my method is that you can deserialize directly to the class you want ("Car") without creating a wrapper class. For example, my way you don't have to have a class like this.

public class CarResponse 
{
  public Car Data { get; set; }
 
  public ApiError Error { get; set; }
}

The downside of my method is you have to do some extra work to parse errors into ApiError instead of Car.

I'm not 100% convinced I want to switch and have a bunch of response wrapper classes, but it's debatable whether there is another person more involved with APIs then John, so I'll probably be switching. ;)

Some questions I have for John's method (UPDATE: see comments for John's answers, my examples have been updated to reflect them).
  1. What's the recommendation for naming the envelop of the success data. Ex. "car" or always use something generic like "data"
  2. On success do you return the error envelop at all? If so, do you indicate a "no error" status code? Ex. "code: 0"

If you're working with APIs be sure to checkout John's excellent tool Runscope. It's good stuff.

Saturday, November 27, 2010

.NET Zencoder API Wrapper

I've been working with automated video encoding systems for over 10 years. I'd like to say there have been lots of improvements in the tools available but the only tools that have made life easier are the hosted solutions like Zencoder.

Zencoder has a really nice API builder on their website and tools like John Sheehan's RestSharp make calling web services from .NET easier then ever. But why not take it a step further and get it down to one line of code to submit an encoding job? That's what I've done over my Thanksgiving weekend and the results are on GitHub in my ZencoderWrapper repository.

One liner transcode:
JobResponse job = new ZencoderClient(API_KEY).SubmitJob("http://cdeutsch.com/input.avi", "ftp://ftpuser:p4ssw0rd@cdeutsch.com/videos/", "output.mp4");

You'll need to create an account on Zencoder to use the service. And if you want to encode more then 5 seconds of video you'll need to pick a payment option. You can alternatively use the ZencoderWrapper to create your account:
CreateAccountRequest account = new CreateAccountRequest("youremail@test.com", "password123");

Here's a more advanced example of creating an Ogg Vorbis file with thumbnails and notifications:
ZencoderClient client = new ZencoderClient(API_KEY);
JobRequest jobRequest = new JobRequest("http://cdeutsch.com/input.avi", new OutputSetting("ftp://ftpuser:p4ssw0rd@cdeutsch.com/videos/", "output.ogg"));
//configure output settings.
jobRequest.outputs[0].audio_codec = AudioCodecSetting.Vorbis;
jobRequest.outputs[0].video_codec = VideoCodecSetting.Theora;
//add a notification.
jobRequest.outputs[0].NotificationSettings = new List<NotificationSetting>();
jobRequest.outputs[0].NotificationSettings.Add(new NotificationSetting(NotificationType.Email, "youremail@test.com"));
//create thumbnails
jobRequest.outputs[0].thumbnails = new ThumbnailSetting();
jobRequest.outputs[0].thumbnails.base_url = "ftp://ftpuser:p4ssw0rd@cdeutsch.com/thumbs/";
jobRequest.outputs[0].thumbnails.format = ThumbnailFormatSetting.PNG;
jobRequest.outputs[0].thumbnails.interval = 5;
jobRequest.outputs[0].thumbnails.number = 3;
jobRequest.outputs[0].thumbnails.prefix = "thumb_";
jobRequest.outputs[0].thumbnails.size = "120x80";
jobRequest.outputs[0].thumbnails.start_at_first_frame = true;
//submit the job
JobResponse job = client.SubmitJob(jobRequest);

Here's how to check the status of the job we just created:
//get job details.
JobListingResponse job = client.GetJob(job.id);

Here's how to check to the status of the job's output file (there can be multiple outputs):
//get progress of first (in this case only) output file.
JobOutputProgressResponse progress = client.GetJobOutputProgress(job.outputs[0].id);

And finally here's how to get a list of all of your jobs:
//get list of jobs
List<JobListingResponse> jobList = client.ListJobs();

If I can convince Implex (the company I work for) to outsource their encoding to Zencoder I'll be using this wrapper for their QwikCast product. I also have another personal project I plan to use the wrapper in.

If you end up using it shoot me a message I'd love the feedback!

UPDATE 11/28/2010:
Not sure how I overlooked this earlier (either missed the Zencoder "Libraries" link or just got carried away with the idea of trying something new I guess) but Chad Burggraf has already created an excellent C# Zencoder client. I may end up using his library over my own for the asynchronous support. Part of it will depend on if Chad's library can compile with Mono since the personal project I want to build will ultimately be running on OSX. I'm not sure if my library will compile in Mono "as-is", but I believe RestSharp will and I don't think I used anything special beyond what's contained in RestSharp.

Thursday, October 7, 2010

Upgrade ASP.NET MVC 3 Preview to MVC Beta

I previously blogged about upgrading the TekPub ASP.NET 2 Starter Site from MVC2 to MVC3 Preview:
http://blog.cdeutsch.com/2010/08/upgrade-mvc2-to-mvc3.html

Today I upgrade that project to MVC3 Beta.

It was pretty easy. Biggest change was with Dependency Injection. IMvcServiceLocator no longer exits and has been replaced with IDependencyResolver.

My NinjectServiceLocator file has been replaced with NinjectResolver (thanks Jedidja from StackOverflow!) and looks like this:

namespace Mvc3Ninject.Utility
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using Ninject;

    [System.Diagnostics.DebuggerStepThrough]
    public class NinjectResolver : IDependencyResolver
    {
        private static IKernel kernel;

        public NinjectResolver()
        {
            kernel = new StandardKernel();
            RegisterServices(kernel);
        }

        public NinjectResolver(IKernel myKernel)
        {
            kernel = myKernel;
            RegisterServices(kernel);
        }

        public static void RegisterServices(IKernel kernel)
        {
            //kernel.Bind<IThingRepository>().To<SqlThingRepository>();
        }

        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }
    }

}

And then in the Global.asax.cs file I changed this line....

//// Tell ASP.NET MVC 3 to use our Ninject DI Container 
MvcServiceLocator.SetCurrent(new NinjectServiceLocator(_container));

...to...

//// Tell ASP.NET MVC 3 to use our Ninject DI Container 
DependencyResolver.SetResolver(new NinjectResolver(_container));

And that's that! Have fun coding!

Here's a complete working sample project based on my Code-First Entity Framework upgrade of the TekPub Starter Site.

Friday, April 16, 2010

Visa Fail with Authorize.NETs CIM

Just heard from a client that their "Save Credit Card" feature stopped working. They use Authorize.NET's Customer Information Manager (CIM) to securely save the credit cards.

Suddenly the application was just returning:
3,1,290,There is one or more missing or invalid required fields.
Why two, large, successful companies like VISA and Authorize.NET can't tell you which fields they want is beyond comprehension.

According to this message thread:
Visa has a mandate in place that allows us to do $0.00 authorizations to test the validity of a card, but in order to do it they require that an address be passed also. Since VISA is the only company that has this policy, the other card types will be unaffected and not experience the same error.
Up until recently they only required Card Number, Expiration, and Security Code. I tried using a dummy address like the message thread suggested. This would only work if the Authorize.NET Address Verification Service settings were configured to ignore the address, so in my case we'll likely be updating the code to pass the billing address in since we have it anyway.

Thanks for the notice on this VISA and Authorize.NET! You have to wonder how much time and money changes like this cost all the organizations affected?

Sunday, February 28, 2010

.NET LINQ Bug - Specified cast is not valid.

I ran into this nasty little bug last week. It didn't show up on my local work station running Windows 7 but it showed up once published to our development server running Windows 2003 Server. It would happen when inserting a record to the database after calling "db.SubmitChanges()"

The root cause was having a "non-integer" key which I rarely use, but this instance was special. I was able to fix it immediately by deleting the association in the LINQ designer. I was lucky because so far I don't need this relationship defined in LINQ.

After the immediate fix I noticed our development server was missing some patches, so I put in a request to have those installed. I haven't yet tested if that fixes the problem but I suspect it will. Also there is this hotfix that may work:
https://connect.microsoft.com/VisualStudio/feedback/details/351358/invalidcastexception-on-linq-db-submit-with-non-integer-key

Hope this saves someone else some time!