Custom operations (Actions and Functions)
allow encapsulating logic for modifying or requesting data that goes
beyond simple CRUD described in the preceding sections of this chapter.
See Action
, ActionImport
, Function
, and FunctionImport
in
OData-CSDLJSON or OData-CSDLXML.
Actions and Functions MAY be bound to any type or collection, similar to defining a method in a class in object-oriented programming. The first parameter of a bound operation is the binding parameter.
The namespace- or alias-qualified name of a bound operation may be appended to any URL that identifies a resource whose type matches, or is derived from, the type of the binding parameter. The resource identified by that URL is used as the binding parameter value. Only aliases defined in the metadata document of the service can be used in URLs.
::: example
Example ##ex: the function MostRecentOrder
can be bound to any URL that
identifies a SampleModel.Customer
<Function Name="MostRecentOrder" IsBound="true">
<Parameter Name="customer" Type="SampleModel.Customer" />
<ReturnType Type="SampleModel.Order" />
</Function>
:::
::: example
Example ##ex: invoke the MostRecentOrder
function with the value of the
binding parameter customer
being the entity identified by
http://host/service/Customers(6)
GET http://host/service/Customers(6)/SampleModel.MostRecentOrder()
:::
::: example
Example ##ex: the function Comparison
can be bound to any URL that
identifies a collection of entities
<Function Name="Comparison" IsBound="true">
<Parameter Name="in" Type="Collection(Edm.EntityType)" />
<ReturnType Type="Diff.Overview" />
</Function>
:::
::: example
Example ##ex: invoke the Comparison
function on the set of red products
GET http://host/service/Products/$filter(Color eq 'Red')/Diff.Comparison()
:::
A bound operation with a single-valued binding parameter can be applied
to each member of a collection by appending the path segment /$each
to
the resource path of the collection, followed by a forward slash and the
namespace- or alias-qualified name of the bound operation. In this case
the type of the collection members MUST match or be derived from the
type of the binding parameter.
The resource path of the collection MAY contain type-cast or filter segments to subset the collection.
The response is a collection with members that are instances of the result type of the bound operation. If the bound operation returns a collection, the response is a collection of collections.
::: example
Example ##ex: invoke the MostRecentOrder
function on each entity in the
entity set Customers
GET http://host/service/Customers/$each/SampleModel.MostRecentOrder()
:::
The client MAY specify the continue-on-error
preference, in which case
the service MAY continue processing actions after a failure. In this
case, the service MUST, regardless of the return
preference, return a
response containing at least the members identified by the request for
which the action failed. Such members MUST be annotated with term
Core.DataModificationException
with a failedOperation
value of
invoke
.
If the continue-on-error
preference has not been specified, and the
service is unable to invoke the action against all of the entities
identified by the request, then it MUST return an error response and
MUST NOT apply the action to any of the members of the collection.
Services MAY return actions and/or functions bound to a particular entity or entity collection as part of the representation of the entity or entity collection within the payload. The representation of an action or function depends on the format.
::: example
Example ##ex: given a GET
request to
http://host/service/Customers('ALFKI')
, the service might respond with
a Customer that includes the SampleEntities.MostRecentOrder
function
bound to the entity
{
"@context": ...,
"CustomerID": "ALFKI",
"CompanyName": "Alfreds Futterkiste",
"#SampleEntities.MostRecentOrder": {
"title": "Most Recent Order",
"target": "Customers('ALFKI')/SampleEntities.MostRecentOrder()"
},
...
}
:::
An efficient format that assumes client knowledge of metadata may omit actions and functions from the payload whose target URL can be computed via metadata following standard conventions defined in OData-URL.
Services can advertise that a function or action is not available for a particular instance by setting its value to null.
::: example
Example ##ex: the SampleEntities.MostRecentOrder
function is not
available for customer 'ALFKI'
{
"@context": ...,
"CustomerID": "ALFKI",
"CompanyName": "Alfreds Futterkiste",
"#SampleEntities.MostRecentOrder": null,
...
}
:::
Functions are operations exposed by an OData service that MUST return data and MUST have no observable side effects.
To invoke a function bound to a resource, the client issues a GET
request to a function URL. A function URL may be
obtained from a previously
returned entity representation or constructed by appending the
namespace- or alias-qualified function name to a URL that identifies a
resource whose type is the same as, or derived from, the type of the
binding parameter of the function. The value for the binding parameter
is the value of the resource identified by the URL prior to appending
the function name, and additional parameter values are specified using
inline parameter syntax. If the function URL
is obtained from a previously
returned entity representation, parameter aliases
that are identical to the parameter name preceded by an at (@
) sign
MUST be used. Clients MUST check if the obtained URL already contains a
query part and appropriately precede the parameters either with an
ampersand (&
) or a question mark (?
).
Services MAY additionally support invoking functions using the
unqualified function name by defining one or more default
namespaces through the
Core.DefaultNamespace
term
defined in OData-VocCore.
Functions can be used within $filter
or
$orderby
system query options. Such
functions can be bound to a resource, as described above, or called
directly by specifying the namespace- (or alias-) qualified function
name. Parameter values for functions within
$filter
or
$orderby
are specified according to the
inline parameter syntax.
To invoke a function through a function import the client issues a GET
request to a URL identifying the function import and passing parameter
values using inline parameter syntax. The
canonical URL for a function import is the service root, followed by the
name of the function import. Services MAY support omitting the
parentheses when invoking a function import with no parameters, but for
maximum interoperability MUST also support invoking the function import
with empty parentheses.
If the function is composable, additional path segments may be appended
to the URL that identifies the composable function (or function import)
as appropriate for the type returned by the function (or function
import). The last path segment determines the system query options and
HTTP verbs that can be used with this this URL, e.g. if the last path
segment is a multi-valued navigation property, a POST
request may be
used to create a new entity in the identified collection.
::: example
Example ##ex: add a new item to the list of items of the shopping cart
returned by the composable MyShoppingCart
function import
POST http://host/service/MyShoppingCart()/Items
...
:::
Parameter values passed to functions MUST be specified either as a URL literal (for primitive values) or as a JSON formatted OData object (for complex values, or collections of primitive or complex values). Entity typed values are passed as JSON formatted entities that MAY include a subset of the properties, or just the entity reference, as appropriate to the function.
If a collection-valued function has no result for a given parameter
value combination, the response is the format-specific representation of
an empty collection. If a single-valued function with a nullable
return-type has no result, the service returns
204 No Content
.
If a single-valued function with a non-nullable return type has no
result, the service returns 4xx
. For functions that return a single
entity 404 Not Found
is the appropriate
response code.
For a composable function the processing is stopped when the function
result requires a 4xx
response, and continues otherwise.
Function imports preceded by the $root
literal MAY be used in the
$filter
or
$orderby
system query options, see
OData-URL.
Parameter values are specified inline by appending a comma-separated list of parameter values, enclosed by parenthesis to the function name.
Each parameter value is represented as a name/value pair in the format
Name=Value
, where Name
is the name of the parameter to the function
and Value
is the parameter value.
::: example
Example ##ex: invoke a Sales.EmployeesByManager
function which takes a
single ManagerID
parameter via the function import
EmployeesByManager
GET http://host/service/EmployeesByManager(ManagerID=3)
:::
::: example
Example ##ex: return all Customers
whose City property returns
"Western" when passed to the Sales.SalesRegion
function
GET http://host/service/Customers?
$filter=Sales.SalesRegion(City=$it/City) eq 'Western'
:::
A parameter alias can be used in place of an inline parameter value. The value for the alias is specified as a separate query option using the name of the parameter alias.
::: example
Example ##ex: invoke a Sales.EmployeesByManager
function via the
function import EmployeesByManager
, passing 3 for the ManagerID
parameter
GET http://host/service/EmployeesByManager(ManagerID=@p1)?@p1=3
:::
Services MAY in addition allow implicit parameter
aliases for function imports and for functions that
are the last path segment of the URL. An implicit parameter alias is the
parameter name, optionally preceded by an at (@
) sign. When using
implicit parameter aliases, parentheses MUST NOT be appended to the
function (import) name. The value for each parameter MUST be specified
as a separate query option with the name of the parameter alias. If a
parameter name is identical to a system query option name (without the
optional $
prefix), the parameter name MUST be prefixed with an at
(@
) sign.
::: example
Example ##ex: invoke a Sales.EmployeesByManager
function via the
function import EmployeesByManager
, passing 3 for the ManagerID
parameter using the implicit parameter alias
GET http://host/service/EmployeesByManager?ManagerID=3
:::
Non-binding parameters annotated with the term
Core.OptionalParameter
defined
in OData-VocCore MAY be omitted. If it is annotated and
the annotation specifies a DefaultValue
, the omitted parameter is
interpreted as having that default value. If omitted and the annotation
does not specify a default value, the service is free on how to
interpret the omitted parameter.
The same function name may be used multiple times within a schema, each with a different set of parameters. For unbound overloads the combination of the function name and the unordered set of parameter names MUST identify a particular function overload. For bound overloads the combination of the function name, the binding parameter type, and the unordered set of names of the non-binding parameters MUST identify a particular function overload.
All unbound overloads MUST have the same return type. Also, all bound overloads with a given binding parameter type MUST have the same return type.
If the function is bound and the binding parameter type is part of an inheritance hierarchy, the function overload is selected based on the type of the URL segment preceding the function name. A type-cast segment can be used to select a function defined on a particular type in the hierarchy, see OData-URL.
Non-binding parameters MAY be marked as optional by annotating them with
the term
Core.OptionalParameter
defined
in OData-VocCore. All parameters marked as optional
MUST come after any parameters not marked as optional.
A function overload is selected if
- The set of specified parameters exactly matches a function overload, or else
- The set of specified parameters matches a subset of parameters that includes all non-optional parameters of exactly one function overload.
Services SHOULD avoid ambiguity, i.e. the combination of the function
name, the unordered set of non-optional non-binding parameter names,
plus the binding parameter type for bound overloads SHOULD identify a
particular function overload. If there is ambiguity, then services MAY
return 400 Bad Request
with an error response body stating that the
request was ambiguous.
Actions are operations exposed by an OData service that MAY have side
effects when invoked. Actions MAY return data but
MUST NOT be further
composed with additional path segments.
To invoke an action bound to a resource, the client issues a POST
request to an action URL. An action URL may be
obtained from a previously
returned entity representation or constructed by appending the
namespace- or alias-qualified action name to a URL that identifies a
resource whose type is the same as, or derives from, the type of the
binding parameter of the action. The value for the binding parameter is
the resource identified by the URL preceding the action name, and only
the non-binding parameter values are passed in the request body
according to the particular format.
Services MAY additionally support invoking actions using the unqualified
action name by defining one or more default
namespaces through the
Core.DefaultNamespace
term
defined in OData-VocCore.
To invoke an action through an action import, the client issues a POST
request to a URL identifying the action import. The canonical URL for an
action import is the service root, followed by the name of the action
import. When invoking an action through an action import all parameter
values MUST be passed in the request body according to the particular
format.
Non-binding parameters that are nullable or annotated with the term
Core.OptionalParameter
defined
in OData-VocCore MAY be omitted from the request body.
If an omitted parameter is not annotated (and thus nullable), it MUST be
interpreted as having the null
value. If it is annotated and the
annotation specifies a DefaultValue
, the omitted parameter is
interpreted as having that default value. If omitted and the annotation
does not specify a default value, the service is free on how to
interpret the omitted parameter. Note: a nullable non-binding parameter
is equivalent to being annotated as optional with a default value of
null
.
4.01 services MUST support invoking actions with no non-binding parameters and parameterless action imports both without a request body and with a request body representing no parameters, according to the particular format. Interoperable clients SHOULD always include a request body, even when invoking actions with no non-binding parameters and parameterless action imports.
If the action returns results, the client SHOULD use content type negotiation to request the results in the desired format, otherwise the default content type will be used.
The client can request whether any results from the action be returned
using the return Prefer
header.
Actions that create and return a single entity follow the rules for
entity creation and return a Location
header that contains the edit URL or read URL of the
created entity.
Actions without a return type respond with
204 No Content
on success.
To request processing of the action only if the binding parameter value,
an entity or collection of entities, is unmodified, the client includes
the If-Match
header with the latest known ETag value
for the entity or collection of entities. The ETag value for a
collection as a whole is transported in the ETag
header of a
collection response.
::: example
Example ##ex: invoke the SampleEntities.CreateOrder
action using
/Customers('ALFKI')
as the customer (or binding parameter). The values
2
for the quantity
parameter and BLACKFRIDAY
for the
discountCode
parameter are passed in the body of the request. Invoke
the action only if the customer's ETag still matches.
POST http://host/service/Customers('ALFKI')/SampleEntities.CreateOrder
If-Match: W/"MjAxOS0wMy0yMVQxMzowNVo="`
Content-Type: application/json
{
"items": [
{ "product": 4001, "quantity": 2 },
{ "product": 7062, "quantity": 1 },
],
"discountCode": "BLACKFRIDAY"
}
:::
The same action name may be used multiple times within a schema provided there is at most one unbound overload, and each bound overload specifies a different binding parameter type.
If the action is bound and the binding parameter type is part of an inheritance hierarchy, the action overload is selected based on the type of the URL segment preceding the action name. A type-cast segment can be used to select an action defined on a particular type in the hierarchy, see OData-URL.
A Prefer header with a
respond-async
preference allows clients
to request that the service process a Data Service
Request asynchronously.
If the client has specified respond-async
in the request, the service
MAY process the request asynchronously and return a
202 Accepted
response. A service MUST
NOT reply to a Data Service Request with
202 Accepted
if the request has not included the respond-async
preference.
Responses that return 202 Accepted
MUST include a Location
header pointing to a status monitor resource that
represents the current state of the asynchronous processing in addition
to an optional Retry-After
header indicating the
time, in seconds, the client should wait before querying the service for
status. Services MAY include a response body, for example, to provide
additional status information.
A GET
request to the status monitor resource again returns
202 Accepted
response if the asynchronous processing has not finished.
This response MUST again include a Location
header
and MAY include a Retry-After
header to be used for a subsequent request. The
Location
header and optional Retry-After
header may or may not
contain the same values as returned by the previous request.
A GET
request to the status monitor resource returns 200 OK
once the
asynchronous processing has completed. For OData 4.01 and greater
responses, or OData 4.0 requests that include an Accept
header that
does not specify application/http
, the response MUST include the
AsyncResult
response header. Any other headers,
along with the response body, represent the result of the completed
asynchronous operation. If the GET
request to the status monitor
includes an OData-MaxVersion
header with a value of 4.0
and no
Accept
header, or an Accept
header that includes application/http
,
then the body of the final 200 OK
response MUST be represented as an
HTTP message, as described in RFC7230, which is the full
HTTP response to the completed asynchronous operation.
A DELETE
request sent to the status monitor resource requests that the
asynchronous processing be canceled. A 200 OK
or a
204 No Content
response indicates that the asynchronous processing has
been successfully canceled. A client can request that the DELETE
should be executed asynchronously. A 202 Accepted
response indicates
that the cancellation is being processed asynchronously; the client can
use the returned Location
header (which MUST be
different from the status monitor resource of the initial request) to
query for the status of the cancellation. If a delete request is not
supported by the service, the service returns
405 Method Not Allowed
.
After a successful DELETE
request against the status monitor resource,
any subsequent GET
requests for the same status monitor resource
returns 404 Not Found
.
If an asynchronous request is cancelled for reasons other than the
consumers issuing a DELETE
request against the status monitor
resource, a GET
request to the status monitor resource returns
200 OK
with a response body containing a single HTTP response with a
status code in the 5xx Server Error
range indicating that the
operation was cancelled.
The service MUST ensure that no observable change has occurred as a result of a canceled request.
If the client waits too long to request the result of the asynchronous
processing, the service responds with a 410 Gone
or
404 Not Found
.
The status monitor resource URL MUST differ from any other resource URL.