in src/Service/Controllers/RestController.cs [192:311]
private async Task<IActionResult> HandleOperation(
string route,
EntityActionOperation operationType)
{
if (route.Equals(REDIRECTED_ROUTE))
{
return NotFound();
}
Stopwatch stopwatch = Stopwatch.StartNew();
// This activity tracks the entire REST request.
using Activity? activity = TelemetryTracesHelper.DABActivitySource.StartActivity($"{HttpContext.Request.Method} {(route.Split('/').Length > 1 ? route.Split('/')[1] : string.Empty)}");
try
{
TelemetryMetricsHelper.IncrementActiveRequests(ApiType.REST);
if (activity is not null)
{
activity.TrackRestControllerActivityStarted(
Enum.Parse<HttpMethod>(HttpContext.Request.Method, ignoreCase: true),
HttpContext.Request.Headers["User-Agent"].ToString(),
operationType.ToString(),
route,
HttpContext.Request.QueryString.ToString(),
HttpContext.Request.Headers["X-MS-API-ROLE"].FirstOrDefault() ?? HttpContext.User.FindFirst("role")?.Value,
ApiType.REST);
}
// Validate the PathBase matches the configured REST path.
string routeAfterPathBase = _restService.GetRouteAfterPathBase(route);
// Explicitly handle OpenAPI description document retrieval requests.
if (string.Equals(routeAfterPathBase, OpenApiDocumentor.OPENAPI_ROUTE, StringComparison.OrdinalIgnoreCase))
{
if (_openApiDocumentor.TryGetDocument(out string? document))
{
return Content(document, MediaTypeNames.Application.Json);
}
return NotFound();
}
(string entityName, string primaryKeyRoute) = _restService.GetEntityNameAndPrimaryKeyRouteFromRoute(routeAfterPathBase);
// This activity tracks the query execution. This will create a new activity nested under the REST request activity.
using Activity? queryActivity = TelemetryTracesHelper.DABActivitySource.StartActivity($"QUERY {entityName}");
IActionResult? result = await _restService.ExecuteAsync(entityName, operationType, primaryKeyRoute);
RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig();
string dataSourceName = runtimeConfig.GetDataSourceNameFromEntityName(entityName);
DatabaseType databaseType = runtimeConfig.GetDataSourceFromDataSourceName(dataSourceName).DatabaseType;
if (queryActivity is not null)
{
queryActivity.TrackQueryActivityStarted(
databaseType,
dataSourceName);
}
if (result is null)
{
throw new DataApiBuilderException(
message: $"Not Found",
statusCode: HttpStatusCode.NotFound,
subStatusCode: DataApiBuilderException.SubStatusCodes.EntityNotFound);
}
int statusCode = (result as ObjectResult)?.StatusCode ?? (result as StatusCodeResult)?.StatusCode ?? (result as JsonResult)?.StatusCode ?? 200;
if (activity is not null && activity.IsAllDataRequested)
{
HttpStatusCode httpStatusCode = Enum.Parse<HttpStatusCode>(statusCode.ToString(), ignoreCase: true);
activity.TrackRestControllerActivityFinished(httpStatusCode);
}
return result;
}
catch (DataApiBuilderException ex)
{
_logger.LogError(
exception: ex,
message: "{correlationId} Error handling REST request.",
HttpContextExtensions.GetLoggerCorrelationId(HttpContext));
Response.StatusCode = (int)ex.StatusCode;
activity?.TrackRestControllerActivityFinishedWithException(ex, ex.StatusCode);
HttpMethod method = Enum.Parse<HttpMethod>(HttpContext.Request.Method, ignoreCase: true);
TelemetryMetricsHelper.TrackError(method, ex.StatusCode, route, ApiType.REST, ex);
return ErrorResponse(ex.SubStatusCode.ToString(), ex.Message, ex.StatusCode);
}
catch (Exception ex)
{
_logger.LogError(
exception: ex,
message: "{correlationId} Internal server error occured during REST request processing.",
HttpContextExtensions.GetLoggerCorrelationId(HttpContext));
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
HttpMethod method = Enum.Parse<HttpMethod>(HttpContext.Request.Method, ignoreCase: true);
activity?.TrackRestControllerActivityFinishedWithException(ex, HttpStatusCode.InternalServerError);
TelemetryMetricsHelper.TrackError(method, HttpStatusCode.InternalServerError, route, ApiType.REST, ex);
return ErrorResponse(
DataApiBuilderException.SubStatusCodes.UnexpectedError.ToString(),
SERVER_ERROR,
HttpStatusCode.InternalServerError);
}
finally
{
stopwatch.Stop();
HttpMethod method = Enum.Parse<HttpMethod>(HttpContext.Request.Method, ignoreCase: true);
HttpStatusCode httpStatusCode = Enum.Parse<HttpStatusCode>(Response.StatusCode.ToString(), ignoreCase: true);
TelemetryMetricsHelper.TrackRequest(method, httpStatusCode, route, ApiType.REST);
TelemetryMetricsHelper.TrackRequestDuration(method, httpStatusCode, route, ApiType.REST, stopwatch.Elapsed);
TelemetryMetricsHelper.DecrementActiveRequests(ApiType.REST);
}
}