Using curl to POST request into ASP.NET Web Api service

by toni 14. March 2012 20:56

As you already know WCF Web Api is now ASP.NET Web Api. The WCF Web Api has an excellent built in test client which you can use to test your application manually. Just select method, which type of request to send, body format etc. and click Send.

wcf_webapi_testclient

Unfortunately the beta version of ASP.NET Web Api does not have the test client but according to asp.net forums they plan to include it in the final version. But there is even easier and faster way to send requests to your application – curl.

What is curl

curl is a command line tool for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP. curl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, kerberos...), file transfer resume, proxy tunneling and a busload of other useful tricks.

Downloading curl

Go to curl website to download the tool. For this blog post I’m using the version Win32 Generic build version 7.2.4.0 (SSL SSH) binary release (single zip file).

Posting request

For the rest of this post I’m assuming you have unpacked the curl’s zip file into a folder and in this folder you also have file called request.txt which contains the raw json you want to POST. And of course you have your service up and running and waiting for a POST request.

Use the following command line to post the request (using the –v makes the output verbose so you can see what is going on).

curl -v -X POST -d @request.txt http://testserver/api/execute

Running the command produces following type of output

e:\>curl -v -X POST -d @request.txt http://testserver/api/execute
* About to connect() to testserver port 80 (#0)
*   Trying 127.0.0.1...
* connected
* Connected to testserver (127.0.0.1) port 80 (#0)
> POST /api/execute HTTP/1.1
> User-Agent: curl/7.24.0 (i386-pc-win32) libcurl/7.24.0 OpenSSL/0.9.8t zlib/1.2.5 libssh2/1.3.0
> Host: testserver
> Accept: */*
> Content-Length: 343
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 343 out of 343 bytes
< HTTP/1.1 400 Bad Request
< Cache-Control: private
< Content-Length: 350
< Content-Type: application/xml; charset=utf-8
< Server: Microsoft-IIS/7.0
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Date: Wed, 14 Mar 2012 17:36:31 GMT
<
<?xml version="1.0" encoding="utf-8"?><ServiceErrorResponse xmlns:xsd=http://www.w3.org/2001/XMLSchema xmlns:xsi="http://www.w3.org/ 2001/XMLSchema-instance"><Reason>Validation failed</Reason> <Details>Validation failed: &#xD;
-- 'Name' should not be empty.</Details></ServiceErrorResponse>
* Connection #0 to host testserver left intact
* Closing connection #0

Now my service accepts both xml and json but for some reason the request fails (I’m using the Fluent Validation to validate the request). If you look at the Content-Type curl is sending you can see the reason for the error:

> Content-Type: application/x-www-form-urlencoded

That is most likely wrong unless your service is actually expecting data in that format. We can specify custom headers using the –H switch like this.

curl -v -H "Content-Type: application/json" -X POST -d @request.txt http://testserver/api/execute

While you specify the correct content type you should also specify the formats you accept in the response. As you can see you can use the –H option multiple times

curl -v -H "Content-Type: application/json" -H "Accept:application/json" -X POST -d @request.txt http://testserver/api/execute

This time you are greeted with HTTP status code 200 and you can see the response the service returned (I removed the non relevant parts of the command output).

e:\>curl -v -H "Content-Type: application/json" -H "Accept:applicat
ion/json" -X POST -d @request.txt http://testserver/api/execute
> POST /api/execute HTTP/1.1
> Content-Type: application/json
> Accept:application/json
>
* upload completely sent off: 343 out of 343 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
<
{"RequestId" : "client","Values" : [ {"Description" : "Result of the operation","Key" : "ResultCode","Value" : "1"}, {"Description" : "Current limit","Key" : "Limit","Value" : "300"}]}

Prettifying the response

As you can see the response is in json format but it is in single line. If you have large response it is pretty hard to decipher it. This can be fixed using something like Tidy JSON which makes the response look like this.

tidyjson_default

You can use the –palette command line to change the colors.

tidyjson_palette

Using batch file to simplify testing

Now all we have to do is simplify the process a bit. I decided to create simple batch file called post.bat with the following content (everything in the line beginning with curl should be in single line)

@ECHO OFF
set filename=%1
set uri=%2
curl -v -H "Content-Type: application/json" 
-H "Accept:application/json" -X POST 
-d @%filename% %uri% | tidyjson

Now I can post request into any uri by giving the file that contains the request and uri where it should be posted. The response will be piped to tidyjson which will make the response pretty to look at.

post.bat request.txt http://localhost/api/execute

That’s it. Hope this speeds up the testing and development.

Telerik JustDecompile second impressions

by toni 22. February 2012 17:56

Few days ago I wrote about Telerik’s JustDecompile. I was using debug build of assembly and comparing the decompiled result to the original code and to the output produced by ILSpy.

For my surprise there was a significant difference between the output produced by JustDecompile and ILSpy. I got a comment from Telerik on my blog that explained this (you can read the full comment here):

The devil as always is in the details. It's been JustDecompile's philosophy to stay as close to the MSIL in the input assemblies as possible. When you compile something in Debug the MSIL produced by the compiler is identical to the C# produced by JustDecompile in the examples above. If you compile these assemblies in Release then the MSIL will closely resemble original code/ILSpy output. In that case JustDecompile will produce code strongly resembling ILSpy output.

That comment in mind I took the release version of AppSettings and compared the results.

Original code

public static object Convert(Type type, 
    string value, IFormatProvider formatProvider)
{
    // In case the type is e.g. Nullable<int>
    if (type.IsGenericType && type.GetGenericTypeDefinition() == 
        typeof(Nullable<>))
    {
        if (string.Empty == value)
        {
            return (object)null;
        }

        // Type is nullable and we have value is not empty.
        // Get the underlying type and continue the conversion.
        type = Nullable.GetUnderlyingType(type);
    }

    if (type.IsEnum)
    {
        return ConvertEnum(type, value);
    }

    return System.Convert.ChangeType(value, type, formatProvider);
}

private static object ConvertEnum(Type type, string value)
{
    return Enum.Parse(type, value, true);
}

JustDecompile

public static object Convert(Type type, 
    string value, IFormatProvider formatProvider)
{
    if (type.IsGenericType && type.GetGenericTypeDefinition() == 
        typeof(Nullable<>))
    {
        if (string.Empty != value)
        {
            type = Nullable.GetUnderlyingType(type);
        }
        else
        {
            return null;
        }
    }
    if (!type.IsEnum)
    {
        return Convert.ChangeType(value, type, formatProvider);
    }
    else
    {
        return TypeConverter.ConvertEnum(type, value);
    }
}

ILSpy

public static object Convert(Type type, 
    string value, IFormatProvider formatProvider)
{
    if (type.IsGenericType && type.GetGenericTypeDefinition() == 
        typeof(Nullable<>))
    {
        if (string.Empty == value)
        {
            return null;
        }
        type = Nullable.GetUnderlyingType(type);
    }
    if (type.IsEnum)
    {
        return TypeConverter.ConvertEnum(type, value);
    }
    return Convert.ChangeType(value, type, formatProvider);
}

As you can see the output makes more sense. There are no additional flag variables or if else branches. Now I’m wondering what ILSpy does when it shows me the debug version of the assembly. Maybe it is doing some kind of optimization when it decompiles the code? I spent some time trying different options but the end result was always the same.

To sum it all up I like the way Telerik and team behind JustDecompile does things:

It's been JustDecompile's philosophy to stay as close to the MSIL in the input assemblies as possible.

Even though I hope I don’t have to decompile code often it is good to have good tool to do it.

Telerik JustDecompile first impressions

by toni 18. February 2012 22:23

Just downloaded Telerik’s JustDecompile. The installation requires you to give your email address so that was a bit disappointing but let’s see what kind of results it can produce. For these tests I used debug build (AnyCPU) of my AppSettings library (Yes, I need to give some love to that library).

Update 22.2.2012 I compared the results using release build.

 

Original code

public static string GetPropertyValue(
    object instance, 
    PropertyInfo propertyInfo, 
    IFormatProvider formatProvider)
{
    var tempValue = propertyInfo.GetValue(instance, null);
    if (null == tempValue)
    {
        return string.Empty;
    }

    var value = Convert.ToString(tempValue, formatProvider);
    return value;
}

JustDecompile

public static string GetPropertyValue(
    object instance, 
    PropertyInfo propertyInfo, 
    IFormatProvider formatProvider)
{
    string empty;
    object tempValue = propertyInfo.GetValue(instance, null);
    bool flag = null != tempValue;
    if (flag)
    {
        string @value = Convert.ToString(tempValue, formatProvider);
        empty = @value;
    }
    else
    {
        empty = string.Empty;
    }
    return empty;
}

As you can see the decompiled version is more complex. Sometimes that is normal but when you compare JustDecompile with ILSpy you get very different results.

Original code

public static bool IsConnectionString(PropertyInfo propertyInfo)
{
    var attribute = propertyInfo
        .GetCustomAttribute<SettingPropertyAttribute>();
    return null != attribute && attribute.IsConnectionString;
}

JustDecompile

public static bool IsConnectionString(PropertyInfo propertyInfo)
{
    bool isConnectionString;
    SettingPropertyAttribute attribute = propertyInfo
        .GetCustomAttribute<SettingPropertyAttribute>();
    if (attribute == null)
    {
        isConnectionString = false;
    }
    else
    {
        isConnectionString = attribute.IsConnectionString;
    }
    bool flag = isConnectionString;
    return flag;
}

ILSpy

public static bool IsConnectionString(PropertyInfo propertyInfo)
{
    SettingPropertyAttribute attribute = propertyInfo
        .GetCustomAttribute<SettingPropertyAttribute>();
    return attribute != null && attribute.IsConnectionString;
}

How about another example of code that can be used to convert type to another type.

Original code

public static object Convert(
    Type type, 
    string value, 
    IFormatProvider formatProvider)
{
    // In case the type is e.g. Nullable<int>
    if (type.IsGenericType && 
        type.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        if (string.Empty == value)
        {
            return (object)null;
        }

        // Type is nullable and we have value is not empty.
        // Get the underlying type and continue the conversion.
        type = Nullable.GetUnderlyingType(type);
    }

    if (type.IsEnum)
    {
        return ConvertEnum(type, value);
    }

    return System.Convert.ChangeType(value, type, formatProvider);
}

JustDecompile

public static object Convert(
    Type type, 
    string value, 
    IFormatProvider formatProvider)
{
    object obj;
    bool genericTypeDefinition;
    if (!type.IsGenericType)
    {
        genericTypeDefinition = true;
    }
    else
    {
        genericTypeDefinition = !(type.GetGenericTypeDefinition() 
            == typeof(Nullable`1));
    }
    bool empty = genericTypeDefinition;
    if (!empty)
    {
        empty = !(string.Empty == value);
        if (empty)
        {
            type = Nullable.GetUnderlyingType(type);
            empty = !type.IsEnum;
            if (empty)
            {
                obj = Convert.ChangeType(value, 
                    type, formatProvider);
            }
            else
            {
                obj = TypeConverter.ConvertEnum(type, value);
            }
            return obj;
        }
        obj = null;
        return obj;
    }
    empty = !type.IsEnum;
    if (empty)
    {
        obj = Convert.ChangeType(value, type, formatProvider);
    }
    else
    {
        obj = TypeConverter.ConvertEnum(type, value);
    }
    return obj;
}

ILSpy

public static object Convert(
    Type type, 
    string value, 
    IFormatProvider formatProvider)
{
    object result;
    if (type.IsGenericType && 
        type.GetGenericTypeDefinition() == typeof(Nullable<>))
    {
        if (string.Empty == value)
        {
            result = null;
            return result;
        }
        type = Nullable.GetUnderlyingType(type);
    }
    if (type.IsEnum)
    {
        result = TypeConverter.ConvertEnum(type, value);
    }
    else
    {
        result = Convert.ChangeType(value, type, formatProvider);
    }
    return result;
}

Next static method that is used to create new object

Original code

public static AppSettings CreateForAssembly(
    Assembly assembly, FileOption fileOption)
{
    if (null == assembly)
    {
        throw new ArgumentNullException("assembly", 
            "Cannot create AppSettings. Assembly is null.");
    }

    var fullPath = GetPathToConfigurationFile(assembly);

    var appSettings = new AppSettings
    {
        Configuration = OpenConfigurationFile(fullPath, fileOption)
    };

    return appSettings;
}

JustDecompile

public static AppSettings CreateForAssembly(
    Assembly assembly, FileOption fileOption)
{
    bool flag = !(null == assembly);
    if (flag)
    {
        string fullPath = AppSettings.GetPathToConfigurationFile(
             assembly);
        AppSettings appSetting = new AppSettings();
        appSetting.Configuration = 
           AppSettings.OpenConfigurationFile(fullPath, fileOption);
        AppSettings appSettings = appSetting;
        AppSettings appSetting1 = appSettings;
        return appSetting1;
    }
    else
    {
        throw new ArgumentNullException("assembly",
           "Cannot create AppSettings. Assembly is null.");
    }
}

ILSpy

public static AppSettings CreateForAssembly(
    Assembly assembly, FileOption fileOption)
{
    if (null == assembly)
    {
        throw new ArgumentNullException("assembly", 
        "Cannot create AppSettings. Assembly is null.");
    }
    string fullPath = AppSettings
        .GetPathToConfigurationFile(assembly);
    return new AppSettings
    {
        Configuration = AppSettings.OpenConfigurationFile(
           fullPath, fileOption)
    };
}

As you can see ILSpy produces result that is closer to the original code. That doesn’t mean JustDecompile is a bad product. It only means there is room for improvement. I’ve had really good experience using other Telerik’s products and their support has been excellent so I’m sure JustDecompile will grow to be a very good tool. Finally I hope I win the t-shirt :)

How to find out if assembly is compiled as x86, x64 or AnyCPU

by toni 29. July 2011 12:12

When using 3rd party assemblies it is sometimes useful to know if they have been compiled to x86, x64 or AnyCPU. One way to do this is to open ildasm(e.g. from Visual Studio Command Prompt) and open the assembly.

ildasm

After opening the assembly double click the manifest and scroll to bottom where you’ll see .corflags.

corflags

Most of the time we want to find out if the assembly is compiled as x86 or AnyCPU. This is the easiest case. If the assembly is compiled as AnyCPU you’ll see the following.

.corflags 0x00000001    //  ILONLY

If the assembly is compiled as x86 you’ll see the following

.corflags 0x00000003    //  ILONLY 32BITREQUIRED

Comparing AnyCPU to x64 is not so easy because the .corflags is the same for both. This is when the corflags.execomes in handy. You can execute it in Visual Studio Command Prompt like this.

>corflags TomatoBandwidth.dll

Version   : v4.0.30319
CLR Header: 2.5
PE        : PE32
CorFlags  : 1
ILONLY    : 1
32BIT     : 0
Signed    : 0

To find out whether assembly is x86, x64 or AnyCPU you have to look at the PE and 32BIT flags. The following table shows the combinations.

Platform/flags PE 32BIT
AnyCPU PE32 0
x86 PE32 1
x64 PE32+ 0

As you can see with x64 assemblies PE is PE32+ and 32BIT is zero. Next time you have to find out what the platform target is for any given assembly, use the corflags.exe and refer to the table above.

Tags:

Tools