Web API

Introduction

MatchPoint offers a WebApiConfiguration class to provide web service endpoints. The configured endpoints all run under the same base address which points to a standard SharePoint service. Therefore all security aspects and authentication are handled by SharePoint itself.

The basic idea behind the web api configuration is to provide an easy way to configure web api endpoints which can process parameters and optionally return values. When doing this many well-known concepts of MatchPoint (e.g. DataProviders) can be used to make the processing of data easier.

The base URI of the service always looks like this:

<http://myHost/_vti_bin/Colygon.MatchPoint/WebApi.svc>

Note that for the service to work, the following file has to exist:

<15Hive>\ISAPI\Colygon.MatchPoint\WebApi.svc

This file is automatically added when the Colygon.MatchPoint.wsp is installed.

Basic Control Flow

To get a better overall understanding of what’s happening, let’s imagine a request to the following web api service URL is made:

http://myhost:2341/_vti_bin/Colygon.MatchPoint/WebApi.svc/myPathPrefix/myParametersUrlTemplate/foo

The control flow is as follows:

  1. Find the first WebApiConfiguration which matches the host, port and protocol (http://myhost:2341 (WebApplication), configured as WebApplicationUrls) and which has the correct PathPrefix (myPathPrefix)
    • If no matching configuration could be found a 404 response is returned.
  2. In this configuration find the first endpoint for which the RouteUrlTemplate (myParametersUrlTemplate/foo) matches
    • If no matching endpoint could be found a 404 response is returned.
  3. If the endpoint has a RequestValidator configured, validate the request with this validator
    • If the request did not validate successfully a 401 response is returned.
  4. All known information is added to an execution scope, so it’s available via variables:
    • All incoming Headers
      Expression variable: RequestHeaders
    • RouteParameters (extracted from the RouteUrlTemplate)
      Expression variable: RouteParameters
    • Payload (extracted from the actual request payload by using the configured PayloadFormat and optionally the PayLoadTargetType).
      Expression variable: Payload Note: This is only available for requests which can contain a payload (e.g. POST).
  5. If configured, the BeforeActions are executed
  6. If configured, either the ActionExpression or the DataProvider is executed (never both!) and the result is temporary kept
    • The result is stored in the expression variable Result and added to the scope
  7. If configured, the result is transformed according to the ResultPropertyMappings
    • The mapped result is stored in the expression variable MappedResult and added to the scope
  8. If configured, the AfterActions are executed
    • The result of the ActionExpression and the DataProvider can be accessed via the expression variables Result and MappedResult
  9. If the result is of the type IResponseHandler the ProcessResponse method of the result is called, else the result is processed according to the configured ResponseFormat. The processed result / data is then added as the body to the response
  10. The configured ResponseHeaders are added to the response
  11. The response is sent back
    • If the result is null the entity body of the response will be suppressed.

If errors occur while processing these steps a 500 response is returned. Details of the errors (and also other trace messages) are logged and can be seen by using the ULS tool.

Configuration

In the following chapters the most important configuration members are described. Note that some configurations options are omitted because they are either self-explanatory or not that important.

PathPrefix & WebApplicationUrls

An arbitrary number of WebApiConfiguration’s can be created. Each configuration must have a unique PathPrefix configured. The path prefix is added to the base URI of the service:

http://myhost/_vti_bin/Colygon.MatchPoint/WebApi.svc/<myPathPrefix>

If there are multiple configurations which havethe same PathPrefix configured, the first found configuration willbe used.

It's also important to specify to which web applications the configuration should apply by specifying WebApplicationUrls. The selection of the correct web application is important for different reasons:
The service can only be reached via the selected WebApplicationsUrls. Another reason are the security aspects. For example, if the web service should be accessible without any authentication a web application with anonymous access enabled can be created (or an existing one can also be extended) and then selected in the configuration.

EndPoints

Next comes the most important part of the configuration: Endpoints. MatchPoint offers four different endpoint types:

Note that the endpoints which have a payload do have more configuration settings than the one without.

RunAsSystemAccount

By default the code is executed under the user which is authenticated on the web app on which the service runs or under the anonymous user, if anonymous authentication on the web app is enabled. If you check RunAsSystemAccount the code will run under the application pool user.

RouteUrlTemplate

Each endpoint has various configuration options. The most important is probably the RouteUrlTemplate. This template "completes" the URL, by adding other parts to the URL path or by adding query parameters. The requested URL is compared to the RouteUrlTemplate of each EndPoint. The first end point which matches is used. If no matching RouteUrlTemplate can be found a 404 response is returned.

Example template without any parameters:

myRouteUrlTemplate

This would match all this URL: http://<myhost>/_vti_bin/Colygon.MatchPoint/WebApi.svc/<myPathPrefix>/myRouteUrlTemplate


Example template with parameter:

myParamtersUrlTemplate/{myParam}

This would match all URLs which look like this: http://<myhost>/_vti_bin/Colygon.MatchPoint/WebApi.svc/<myPathPrefix>/myParamtersUrlTemplate/<parameterValue> The parameterValue can vary and be an arbiratry string.

The configured parameters can later (e.g. in the ActionExpression) be accessed via RouteParameters["<ParamName>"] or RouteParameters.<ParamName>.


Naturally it’s also possible to make more complex RouteUrlTemplates and to have multiple parameters:

complexTemplate/{param1}?param2={param2}

Which would match something like this:

http://<myhost>/_vti_bin/Colygon.MatchPoint/WebApi.svc/<myPathPrefix>/complexTemplate/foo?param2=bar

RouteParameters

It’s possible to define RouteParameters. Every route parameter has to have the same name as a parameter which is specified in the RouteUrlTemplate. When processing all bound variables and all query parameters which were found within the RouteUrlTemplate, MatchPoint tries to find a corresponding configured RouteParameter. If one is found the parameter value will be converted to the specified ValueType and then the ConstraintExpression is evaluated. If any ConstraintExpression evaluates to false a 404 response is returned. When all ConstraintExpressions evaluated to true all parameters are added to the RouteParameters variable. Variables or parameters for which no corresponding configured RouteParameter exists are directly added to the RouteParameters variable.

The RouteParameters variable can later be accessed in the ActionExpression, the DataProviders, the PropertyMappings and the ResponseHeaders.

ActionExpression & DataProvider

Either the ActionExpression or the DataProvider can be used to process the data and to return a result. In both cases the variables RouteParameters, Payload (only available for endpoints which have a body) and RequestHeaders can be used. The type of the payload depends on the configured PayloadFormat and the PayloadTargetType (optional). If a PayloadTargetType is specified and the PayloadFormat is either Json or Xml the payload is automatically deserialized to the specified target type, else the payload is a Json object or an XmlDocument.

Note that it’s purely optional for the ActionExpression to return a result.

ResultPropertyMappings

It’s possible to configure ResultPropertyMappings which can transform the result, returned by either the ActionExpression or a DataProvider, to a "dictionary-styled" result. If any property mappings are defined the result will be transformed – even if it’s null! The property mapping is especially useful to exclude some properties from the actual result (this may be needed, because not everything is wanted or not even serializable) and also to transform the result to a format which is expected by the client. This is especially useful if Xml or Json is used as the ResponseFormat.

The property mapping can contain normal Mappings and CollectionPropertyMappings. A normal Mapping will just store (and optionally transform) the DataItem in a variable with the configured name whereas the CollectionPropertyMapping allows to specify a part of the data which is a collection (Array, list, etc.). For this data it’s then again possible to define mappings (recursive).

To clarify the usage of the property mappings, let’s assume that our ActionExpression returned the following C# object:

{Name: "Foo", Webs: [SPWeb1, SPWeb2]}

Since this object contains SPWebs we cannot return it directly. Also we’d like to specify directly in the result set how many webs are contained – as is often done by services which return a Json response with multiple items.

This means our configuration will look like this:

<ResultPropertyMappings>
    <Mapping type="Mapping">
        <Name>Name</Name>
        <ValueExpression>
            <![CDATA[DataItem.Name]]>
        </ValueExpression>
    </Mapping>
    <Mapping type="Mapping">
        <Name>NumberOfWebs</Name>
        <ValueExpression>
            <![CDATA[DataItem.Webs.Length]]>
        </ValueExpression>
    </Mapping>
    <Mapping type="CollectionPropertyMapping">
        <ValueExpression>
            <![CDATA[DataItem.Webs]]>
        </ValueExpression>
        <Mappings>
            <PropertyMapping type="PropertyMapping">
                <Name>Name</Name>
                <ValueExpression>
                    <![CDATA[DataItem.Name]]>
                </ValueExpression>
            </PropertyMapping>
            <PropertyMapping type="PropertyMapping">
                <Name>Url</Name>
                <ValueExpression>
                    <![CDATA[DataItem.Url]]>
                </ValueExpression>
            </PropertyMapping>
        </Mappings>
        <Name>Webs</Name>
    </Mapping>
</ResultPropertyMappings>

As can be seen in the configuration snippet only the Name and the Url of the SPWebs are extracted – the rest is ignored. This new result can now be serialized without any problem.

If the ResponseFormat is set to Json the following would be returned:

{
  Name: "Foo",
  NumberOfWebs: 2,
  Webs: [
    {
      Name: "Web 1",
      Url: "http://foo.bar"
    },
    {
      Name: "Web 2",
      Url: "http://bar.foo"
    }
  ]
}

BeforeActions & AfterActions

It's possible to define BeforeActions and AfterActions. The only difference between the two is, that the BeforeActions are executed before the ActionExpression / the DataProvider has been executed whereas the AfterActions are executed after them. In the AfterActions the result of the ActionExpression / DataProvider can be accessed by the expression variables Result and MappedResult (to access the result after the ResultPropertyMappings have been applied).

results matching ""

    No results matching ""