Clone wiki

CodeCop / JSON API

JSON API

CodeCop was built with unobtrusion in mind, so method interception is totally configurable via a JSON file (this file can be manually generated or via our online tool).

This means that anytime you want to add or remove interceptors you don't have to lose time recompiling and deploying the entire app again.

Now onto the configuration file specifcs.

First and foremost, the JSON file must be named copconfig.json and should be placed in the folder where your main assembly resides, if you're in a development environment this folder is the bin folder.

The file should have the following base structure:

{
    "Types": [],
    "GlobalInterceptors": [],
    "Key":""
}

The Types array takes Type objects and these objects should have this structure:

 {
      "TypeName": "Namespace.Class, Assembly",
      "Methods": [],
      "GenericArgumentTypes": []
 }

Notice the Methods array above? This is where you specify the methods and properties you want to intercept. It has the following structure:

 {
      "MethodSignature": "MethodSignature()",
      "Interceptors": [ ]
 }

These are the naming conventions you should use:

  • Constructors: ".ctor()" (a parameterless ctor)

  • Constructor overloads: ".ctor(Int32)" (an overload that takes an integer as argument)

  • Methods: "Method1()" (a method called Method1 with no parameters)

  • Method overloads: "Method1(Int32, System.String)" (an overload of Method1 that takes an integer and a string as arguments)

  • Property Get Method: "get_Property1()" (the get method for a property called Property1)

  • Property Set Method: "set_Property1()" (the set method for a property called Property1)

  • All Methods and Properties: "MethodSignature: *" (just pass an asterisk as the MethodSignature property in a single method element on the methods array)

We encourage you to use our online tool to automatically generate the JSON for you.

The Methods object has an Interceptors array, this is where we specify the interceptor names. Interceptors execute by index order. By convention you should name your interceptor with a Interceptor prefix, but it is not mandatory (please refer to the Interceptors section for more information).

 {
      "MethodSignature": "MethodSignature",
      "Interceptors": ["FooInterceptor", "BarInterceptor" ]
 }

If the Type to intercept its methods is an open generic type e.g.(MyClass<T>) you have to specify on the GenericArgumentTypes array the arguments that the closed version(s) you want to intercept will use. For instance if you want to intercept the method Foo() of MyClass<T> when T is an Int and when T is a Boolean you should do it like this:

{  
     "TypeName": "MyApp.MyClass`1, MyAppAssembly",
     "Methods": [
                {
                    "MethodSignature": "Foo()",
                    "Interceptors": [ "LogMethodParametersInterceptor" ]
                }
            ],
     "GenericArgumentTypes": [ "System.Int32", "System.Boolean"]
}

In case the generic type has more than one generic argument you specify in exactly the same way. If AnotherType<T,U> had 2 generic arguments and we want to intercept the Bar() method on 2 variants of the closed version, the first with System.Int32 and System.Boolean and the second with Decimal and another custom Type called Class1 we would do it like this:

{  
     "TypeName": "MyApp.AnotherType`2, MyAppAssembly",
     "Methods": [
                {
                    "MethodSignature": "Bar()",
                    "Interceptors": [ "GuardNullInterceptor" ]
                }
            ],
     "GenericArgumentTypes": [ "System.Int32", "System.Boolean", "System.Decimal", "MyApp.Class1" ]
}

Its also possible instead of specifying all the types and methods to intercept on the JSON, specify a single common interface and select the methods to intercept. That is called Pipelined Interception. In the following example we are saying that we want to intercept all methods on all types that implement the IFactory interface and apply the "GuardNullInterceptor" on them.

{  
     "TypeName": "MyApp.IFactory, MyAppAssembly",
     "Methods": [
                {
                    "MethodSignature": "*",
                    "Interceptors": [ "GuardNullInterceptor" ]
                }
            ],
     "GenericArgumentTypes": [ ]
}

If the interface is a generic interface, like mentioned above we have to specify the generic arguments that closed types will use. On the below example we are saying that we want to intercept all methods on all types that implement IMyfactory<T> interface, when those types are T1<System.String>, T1<System.Boolean>, T2<System.String>, T2<System.Boolean>.

{  
     "TypeName": "MyApp.IFactory'1, MyAppAssembly",
     "Methods": [
                {
                    "MethodSignature": "*",
                    "Interceptors": [ "GuardNullInterceptor" ]
                }
            ],
     "GenericArgumentTypes": ["System.String", "System.Boolean" ]
}

The GlobalInterceptors array is where you specify which interceptors are transversal to types. They execute on every intercepted method/property.

{
    "Types": [],
    "GlobalInterceptors": ["GlobalFooInterceptor", "GlobalBarInterceptor" ]
}

It is also possible to specify the following regex operators on the GlobalInterceptors array.

  • Starts With : ^=

  • Ends With : $=

  • Contains : ~=

  • Equals : =

{
    "Types": [],
    "GlobalInterceptors": ["GlobalFooInterceptor[MethodName^=.ctor| MethodName~=du]]", "GlobalBarInterceptor[MethodName=Method1(Int32)]"]
}

The Key property is where you put your product key. Leave empty if you want CodeCop to work as Free version. Please read the licensing section for more details.

{
    "Key": "Your product key or leave empty for free product mode",
}

The following is an example of how a copconfig.json file could look like:

{
    "Types": [
        {
            "TypeName": "ConsoleApplication1.FooClass, ConsoleApplication1",
            "Methods": [
                {
                    "MethodSignature": "FooMethod1()",
                    "Interceptors": [ "WriteMethodNameToConsoleInterceptor"]
                },
                {
                    "MethodSignature": "get_Property1()",
                    "Interceptors": [ "WriteMethodNameToConsoleInterceptor"]
                }
            ],
          "GenericArgumentTypes": [ ]
        },
        {
            "TypeName": "ConsoleApplication1.MyClass, ConsoleApplication1",
            "Methods": [
                {
                    "MethodSignature": "Method5()",
                    "Interceptor": [ "FooInterceptor", "BarInterceptor"]
                },
                {
                    "MethodSignature": "Method7(Int32)",
                    "Interceptor": [ "WriteMethodNameToConsoleInterceptor"]
                }
            ],
           "GenericArgumentTypes": [ ]
        },
       {
            "TypeName": "ConsoleApplication1.AnotherClass, ConsoleApplication1",
            "Methods": [
                {
                    "MethodSignature": "*",
                    "Interceptors": [ "WriteMethodNameToConsoleInterceptor" ]
                }

            ],
            "GenericArgumentTypes": [ ]
        },
        {  
            "TypeName": "MyApp.MyClass`1, MyAppAssembly",
            "Methods": [
                {
                    "MethodSignature": "Foo()",
                    "Interceptors": [ "LogMethodParametersInterceptor" ]
                }
            ],
            "GenericArgumentTypes": [ "System.Int32", "System.Boolean"]
     }

    ],
    "GlobalInterceptors": ["GlobalFooInterceptor", "GlobalBarInterceptor[MethodName=~Foo]"],
    "Key":"Your product key or leave empty for free product mode"

}

NEW FEATURES LOG:

From version 1.2.6 onwards if the assembly where your type resides is outside of the bin folder, e.g. the GAC you can specify it's full path on an optional property, like so:

 {
      "TypeName": "Namespace.Class",
      "Methods": [],
      "GenericArgumentTypes": [],
      "AssemblyPath":"C:\\Windows\\Microsoft.NET\\assembly\\GAC_MSIL\\Foo\\v4.0_1.0.0.0__19b7b37c1bacf8ce\\Foo.dll"
 }

From Version 1.3.2 onwards a new "Disabled" property was added that forces CodeCop not to intercept methods. Use it like so:

{
"Disabled": "true"
}

From Version 1.3.3 onwards a new "AssemblyLoadConstraints" property was added that forces CodeCop assembly discovery mechanism to be constrained to certain assemblies only. This is how we would configure CodeCop only to load and inspect the foo.dll and the bar.dll assembly. This is very useful for applications that have lots of assemblies inside the bin folder and speeds up the bootstrapping process.

{
  "Types": [

    {
      "TypeName": "ConsoleTests.Program, ConsoleTests",
      "Methods": [
        {
          "MethodSignature": "Test()",
          "Interceptors": [ "BeforeAfterInterceptor" ]
        }
      ],
      "GenericArgumentTypes": [ ],
      "AssemblyPath": ""
    }
  ],
  "GlobalInterceptors": [ ],
  "Key": "",
  "AssemblyLoadConstraints": ["foo", "bar"]
}

Updated