Using the Management API with Contentful and .NET
The Content Management API (CMA) is a restful API for managing content in your Contentful spaces. You can create, update, delete and retrieve content using well-known HTTP verbs.
To make development easier for our users, we publish client libraries for various languages which make the task easier. This article details how to use the .NET client library to create, update and delete content.
Pre-requisites
This tutorial assumes you understand the basic Contentful data model as described in the developer center and that you have already read and understand the getting started tutorial for the .NET client library.
Contentful.net is built on .NET Core and targets .NET Standard 2.0. The client library is cross-platform and runs on Linux, macOS and Windows.
Your first request
To communicate with the CMA we use a similar approach as when we call the CDA, but instead of a ContentfulClient you use a ContentfulManagementClient that requires three parameters.
- An
HttpClientthat makes the HTTP requests to the CMA. - An access token. The token has to be a valid management token created using OAuth. To learn more about creating a management token please refer to the documentation.
- A space id. The id is the unique identifier of your space that you can find in the Contentful web app. This will be the default space for all operations in the client library, but you can also specify a different space for every operation.
HttpClient in .NET is special. It implements IDisposable but is generally not supposed to be disposed of for the lifetime of your application. This is because whenever you make a request with the HttpClient and immediately dispose of it, you leave the connection open in a TIME_WAIT state. It will remain in this state for 240 seconds by default. If you make a lot of requests in a short period you might end up exhausting the connection pool, which would result in a SocketException. To avoid this you should share a single instance of HttpClient for the entire application, and expose the underlying HttpClient of the ContentfulManagementClient allows you to do this.Once you have an ContentfulManagementClient you can start managing content. For example, to create a brand new space:
If your user account belongs to a single organization, you can omit the organization_id parameter.
To delete a space, pass a space id to the DeleteSpace method:
To change the name of an existing space, use the UpdateSpaceName method.
Unless your account has more than one organization, you can omit the organization id, but the version parameter is always needed.
This is a common pattern to update operations in the Contentful management API. To update an entry, you need to pass the last known version to make sure that you do not overwrite a resource that has since been updated. This is called ‘optimistic locking’ and prevents unwanted data loss. If the version passed does not match the latest version in Contentful the update will be rejected and a ContentfulException thrown.
To retrieve the version of a resource, inspect the SystemProperties.Version property.
The following is an example of updating a space:
Working with content types
Once you’ve familiarized yourself with creating and deleting spaces, the next step is to add content types to your space. A content type in Contentful is a blueprint for an entry and contains up to 50 fields that you can define.
First create a new ContentType object, initialize it’s system properties, give it an ID, name, and description:
Create a List of field types, and add all the fields for your content model to it. The example below shows you how to recreate the ‘Product’ content type you find in our examples spaces, further explanation of the fields follows:
Define which field is the display field, and send the new content type declaration to the client.
The fields have a lot of properties and can look daunting at first, especially if you add validations, so let’s break the components down. Every field can consist of up to 10 properties.
But at a minimum, you need to specify the name, id, and type.
Field validations
The most complex part of fields is handling validations. You can use different validators that all implement the IFieldValidator interface. Every validator has a Message property where you can specify a custom message to show if the validation fails.
LinkContentTypeValidator
The LinkContentTypeValidator validates that a given field contains entries of a particular content type. The constructor takes an optional message and any number of string ids or content types to validate against.
InValuesValidator
The InValuesValidator validates that a given field value is within a predefined set of values. The constructor takes an optional message and any number of strings to validate against.
MimeTypeValidator
The MimeTypeValidator validates that an asset is of a particular mime type group.
Available restrictions are:
MimeTypeRestriction.AttachmentMimeTypeRestriction.PlaintextMimeTypeRestriction.ImageMimeTypeRestriction.AudioMimeTypeRestriction.VideoMimeTypeRestriction.RichtextMimeTypeRestriction.PresentationMimeTypeRestriction.SpreadsheetMimeTypeRestriction.PdfDocumentMimeTypeRestriction.ArchiveMimeTypeRestriction.CodeMimeTypeRestriction.Markup
SizeValidator
The SizeValidator validates that an array field contains a specific number of items.
Both the min and the max value are nullable. You can create size validators that validate that an array contains at least a set number of items, but without an upper bound. Or contains a maximum of a set number of items but may also be empty.
RangeValidator
The RangeValidator validates that a field is within a particular numeric range.
When used for text fields it validates that the entered value contains at least the minimum number of characters and at most the maximum number of characters. For numeric fields, it validates that the value entered is within the specified range. Both the min and max value are nullable in the same way as for the SizeValidator.
RegexValidator
The RegexValidator validates that a field conforms to a specified regular expression.
UniqueValidator
The UniqueValidator validates that the field values is unique among all entries.
Activate a content type
Once you have created a content type you need to activate it before it’s usable.
You can deactivate the content type in a similar fashion, but you don’t need to specify a version as there is no risk of data loss.
Deleting a content type is similar, you must deactivate a content type before deleting it.
There are three methods available to fetch content types.
Editor interface
An editor interface represents information about how the user interface displays the fields of a content type.
Every content type has its own Editor interface, and you cannot explicitly create it. Instead, you retrieve and update it appropriately.
Once you have the editor interface, you can update it and change how certain fields are displayed.
An Editor interface consists of a collection of ‘controls’. These are of type EditorInterfaceControl which has three different properties.
The FieldId is the id of the field that this EditorInterfaceControl controls the appearance of, and the WidgetId is the widget type you want the control to display as. There’s a handy SystemWidgetIds class that contains all built in ids, for a full list refer to the API documentation.
The Settings property contains settings for certain widget types. Normally it’s of type EditorInterfaceControlSettings and has only a HelpText property which represents the help text you want to display in relation to your field control. There are three distinct subclasses of EditorInterfaceControlSettings for specific fields.
Working with entries
You fetch entries in a similar way to using the Content Delivery API (CDA), but with three key differences:
- Every entry will always include all configured locales.
- Calls will include unpublished entries.
- The CMA does not cache calls as rigorously as the CDA.
For these reasons, it’s better to use the CDA and the ContentfulClient provided by the .NET client library if you’re only fetching content. At times it can be convenient to use the CMA as well.
For example, to get all entries for a space you can pass a QueryBuilder to filter which entries to return.
Or to get a single entry.
Entry<dynamic>, as opposed to the GetEntry method of the ContentfulClient.To create (or update) an entry use the CreateOrUpdateEntry method. Since you need to provide all the locales the simplest way to model fields is with dictionaries.
To create reference fields to other entries or assets you need to mimic the JSON structure of the Contentful API where references look like this:
You can model this in your Entry<dynamic> like this:
There are also strongly typed helper classes available to model a reference.
You do not need to use Entry<dynamic> when creating or updating entries. There are generic methods available that let you use a custom type instead.
If you wish to update just a specific locale for an entry you can use the UpdateEntryForLocale method. In this case you do not need to wrap your properties in dictionaries.
You can publish/unpublish, archive/unarchive and delete entries.
For example, to publish the specified version of an entry and make it publicly available through the CDA.
Or to unpublish a specified version.
To archive an entry. You can only archive an entry if it’s not published.
To unarchive an entry.
To permanently delete an entry.
Working with assets
You fetch assets in a similar way to fetching entries. It includes all the locales for an asset, unpublished assets, and calls are not cached to the same level.
The ContentfulManagementClient returns ManagementAssets as opposed to the Assets returned from ContentfulClient. This is because every property is a Dictionary containing the value for each locale.
To create an asset, initialize a ManagementAsset and pass it to the CreateOrUpdateAsset method.
After you have created an asset, you need to process it, which moves it to the Contentful AWS buckets and CDN servers.
As with entries, you can publish/unpublish, archive/unarchive and delete assets.
To publish a particular version of the asset and make it publicly available through the CDA.
To unpublish a specified version.
To archive an entry. You can only archive an asset if it’s not published.
To unarchive an asset.
To permanently delete an asset.
Uploading files directly
At times you want to upload a file directly from disk or
some other source. You then use the UploadReference property of your ManagementAsset. To create an UploadReference use the UploadFile method.
First upload your binary file.
This returns an UploadReference that can then be used when creating an asset. You need to remove a few properties from the SystemProperties of the reference. This is because these properties are not allowed when creating assets.
The asset then needs to be processed as in the previous example.
This way of creating asssets through an upload is quite arduous. The .NET client library therefore provides a way to create, upload and process a file in one call by using the UploadFileAndCreateAsset method.
This will upload the file, create the asset, associate the upload with the asset and finally process the asset. It is then ready to be published.
You can also get and delete a previously uploaded UploadReference.
Working with locales
Locales allow you to define translatable fields for entries and assets. To fetch all configured locales for a space, use the GetLocalesCollection method.
To create a locale you need to define some properties.
You can’t delete a locale used as a fallback. You first need to delete or update any locale that has the locale you’re trying to delete set as a fallback. When you delete a locale you delete all content associated with that locale. It’s not possible to reverse this action and all content will be permanently deleted.