英文:
AspNetCore.HealthCheck in Azure function always returns 200 even when unhealthy
问题
我们有一个使用.Net Core 7的Azure函数,我们已经添加了一个HealthCheck。如果状态设置为不健康,健康检查会触发,我们返回一个 ServiceUnavailableObjectResult
的实例。无论我们返回什么,HTTP状态码始终返回200。
来自 Program.cs:
services.AddHealthChecks()
.AddUrlGroup(
uri: new Uri($"{baseAddress}/health"),
name: "Mgmt")
.AddAzureBlobStorage(
connectionString: context.Configuration.GetValue<string>(RecipientUploadOptions.BlobConnectionStringName),
containerName: context.Configuration.GetValue<string>(RecipientUploadOptions.IncomingContainerName),
name: "IncomingBlobContainer")
.AddAzureBlobStorage(
connectionString: context.Configuration.GetValue<string>(RecipientUploadOptions.BlobConnectionStringName),
containerName: context.Configuration.GetValue<string>(RecipientUploadOptions.ErrorContainerName),
name: "ErrorBlobContainer")
.AddAzureBlobStorage(
connectionString: context.Configuration.GetValue<string>(RecipientUploadOptions.BlobConnectionStringName),
containerName: context.Configuration.GetValue<string>(RecipientUploadOptions.ArchiveContainerName),
name: "ArchiveBlobContainer");
来自 HealthCheckFunction.cs:
[Function("health")]
public async Task<IActionResult> Health([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData z)
{
var status = await _healthCheckService.CheckHealthAsync();
if (status.Status == HealthStatus.Healthy)
{
return new OkObjectResult(Enum.GetName(typeof(HealthStatus), status.Status));
}
return new ServiceUnavailableObjectResult(Enum.GetName(typeof(HealthStatus), status.Status));
}
期望的结果:
- 返回的HTTP状态码为503 - 服务不可用,以及有效负载。
实际结果:
- 返回的HTTP状态码是200 - 正常,以及有效负载。
- 在Web API项目中用于HealthChecks的相同代码按预期返回503。
任何建议将不胜感激。
英文:
We have an Azure Function using .Net Core 7 that we've added a HealthCheck to. The healthcheck fires, if the status is set to unhealthy, we return a an instance of ServiceUnavailableObjectResult
. The HTTP status code always returns 200 regardless of what we return.
From Program.cs
services.AddHealthChecks()
.AddUrlGroup(
uri: new Uri($"{baseAddress}/health"),
name: "Mgmt")
.AddAzureBlobStorage(
connectionString: context.Configuration.GetValue<string>(RecipientUploadOptions.BlobConnectionStringName),
containerName: context.Configuration.GetValue<string>(RecipientUploadOptions.IncomingContainerName),
name: "IncomingBlobContainer")
.AddAzureBlobStorage(
connectionString: context.Configuration.GetValue<string>(RecipientUploadOptions.BlobConnectionStringName),
containerName: context.Configuration.GetValue<string>(RecipientUploadOptions.ErrorContainerName),
name: "ErrorBlobContainer")
.AddAzureBlobStorage(
connectionString: context.Configuration.GetValue<string>(RecipientUploadOptions.BlobConnectionStringName),
containerName: context.Configuration.GetValue<string>(RecipientUploadOptions.ArchiveContainerName),
name: "ArchiveBlobContainer");
From HealthCheckFunction.cs
[Function("health")]
public async Task<IActionResult> Health([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData z)
{
var status = await _healthCheckService.CheckHealthAsync();
if (status.Status == HealthStatus.Healthy)
{
return new OkObjectResult(Enum.GetName(typeof(HealthStatus), status.Status));
}
return new ServiceUnavailableObjectResult(Enum.GetName(typeof(HealthStatus), status.Status));
}
Expected:
- The HTTP status code returned is 503 - Service Unavailable along with the payload.
Actual:
- The HTTP status code returned os 200 - OK along with the payload
- The same code used for HealthChecks in web API projects return 503 as expected
Any suggestions would be greatly appreaciated
答案1
得分: 1
有两种方式可以在使用.NET Isolated Worker时开发HTTP函数。
内置HTTP模型
对于这种方式,您需要返回如下的HttpResponseData类型:
[Function(nameof(Health))]
public async Task<HttpResponseData> Health([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
var response = req.CreateResponse();
var status = await _healthCheckService.CheckHealthAsync();
await response.WriteAsJsonAsync(
Enum.GetName(typeof(HealthStatus), status.Status),
status.Status == HealthStatus.Healthy ? HttpStatusCode.OK : HttpStatusCode.ServiceUnavailable
);
return response;
}
ASP.NET Core集成(预览版)
要使这个模型工作,需要满足一些先决条件,这些条件在上面链接的文档中有详细说明。以下是供参考的同样要求:
-
安装 Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore NuGet 包,版本为 1.0.0-preview2 或更高(请注意,您必须在 Visual Studio 中勾选“包括预发行版本”复选框)
此外,请确保 Microsoft.Azure.Functions.Worker.Sdk 版本为 1.11.0 或更高,以及 Microsoft.Azure.Functions.Worker 版本为 1.16.0 或更高。
-
在
Program.cs
中将ConfigureFunctionsWorkerDefaults
替换为ConfigureFunctionsWebApplication
-
使用 ASP.NET 中的
HttpRequest
和IActionResult
类型 -
添加名为
AzureWebJobsFeatureFlags
的应用设置,值为EnableHttpProxying
英文:
There are two ways you can develop HTTP functions when using .NET Isloted Worker.
Built-in HTTP Model
For this you need to return the HttpResponseData type as follows
[Function(nameof(Health))]
public async Task<HttpResponseData> Health([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
var response = req.CreateResponse();
var status = await _healthCheckService.CheckHealthAsync();
await response.WriteAsJsonAsync(
Enum.GetName(typeof(HealthStatus), status.Status),
status.Status == HealthStatus.Healthy ? HttpStatusCode.OK : HttpStatusCode.ServiceUnavailable
);
return response;
}
ASP.NET Core integration (preview)
There are a few pre-requisites to get this model to work which are documented in the doc linked above. Here are the same for reference.
-
Install the Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore NuGet package, version 1.0.0-preview2 or later (Note that you must check the
include prerelease
checkbox in Visual Studio)Also, make sure Microsoft.Azure.Functions.Worker.Sdk is version 1.11.0 or later and Microsoft.Azure.Functions.Worker is version 1.16.0 or later.
-
Replace
ConfigureFunctionsWorkerDefaults
withConfigureFunctionsWebApplication
inProgram.cs
-
Use
HttpRequest
andIActionResult
types from ASP.NET -
Add the
AzureWebJobsFeatureFlags
app setting with valueEnableHttpProxying
答案2
得分: 0
以下是基于Pramod的答案实现的代码部分的翻译:
[Function(nameof(Health))]
public async Task<HttpResponseData> Health([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
//HACK (解决方法)
//由于Azure Function框架在健康检查中的行为
//无论状态如何都会返回200,忽略了
//IActionResult。暂时的解决方案是更接近底层
//并编写我们自己的响应主体。ASP.NET Core集成(用于Azure
//Functions)仍处于预览阶段(现在还不能很好地解决问题
//但可能在未来更好地解决这个问题)。
var response = req.CreateResponse();
var status = await _healthCheckService.CheckHealthAsync();
var httpStatus = (status.Status == HealthStatus.Healthy
? HttpStatusCode.OK
: HttpStatusCode.ServiceUnavailable);
var statusResult = new
{
Value = Enum.GetName(typeof(HealthStatus), status.Status),
Formatters = new string[] { },
// Entries = status.Entries,
// ^^^^ 这会很好,但不能直接序列化并且会
// 抛出NotSupportedException。这可能很有用
// 因为它提供了有关什么不健康的详细信息
ContentType = "application/json",
StatusCode = httpStatus
};
await response.WriteAsJsonAsync(statusResult, httpStatus);
return response;
}
英文:
Here is implementation based on Pramod's answer here.
[Function(nameof(Health))]
public async Task<HttpResponseData> Health([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
//HACK (Work around)
//Due to a behavior in the Azure Function framework for health checks
//will always return a 200 regardless of state and ignore the
//IActionResult. The solution--for now--is to get closer to the metal
//and write our own response body. ASP.NET Core integration (for Azure
//Functions) is still in preview (and not solving the problem for us
//right this moment but may hold the key for doing this better in the
//future.
var response = req.CreateResponse();
var status = await _healthCheckService.CheckHealthAsync();
var httpStatus = (status.Status == HealthStatus.Healthy
? HttpStatusCode.OK
: HttpStatusCode.ServiceUnavailable);
var statusResult = new
{
Value = Enum.GetName(typeof(HealthStatus), status.Status),
Formatters = new string[] { },
// Entries = status.Entries,
// ^^^^ This would be nice but doesn't directly serialize and will
// throw a NotSupportedException. That may be useful to explore
// since it gives details about what is unhealthy
ContentType = "application/json",
StatusCode = httpStatus
};
await response.WriteAsJsonAsync(statusResult, httpStatus);
return response;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论