Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# KeyVault GetSecretAsync永远不会返回_C#_.net_Azure_Async Await - Fatal编程技术网

C# KeyVault GetSecretAsync永远不会返回

C# KeyVault GetSecretAsync永远不会返回,c#,.net,azure,async-await,C#,.net,Azure,Async Await,在web应用程序中使用KeyVault的示例代码包含以下代码: public static async Task<string> GetSecret(string secretId) { var secret = await keyVaultClient.GetSecretAsync(secretId); return secret.Value; } 不幸的是,调用永远不会返回,查询将无限挂起 原因可能是什么,因为坦白说我不知道从哪里开始 不幸的是,调用从未返回,查

在web应用程序中使用KeyVault的示例代码包含以下代码:

public static async Task<string> GetSecret(string secretId)
{
    var secret = await keyVaultClient.GetSecretAsync(secretId);
    return secret.Value;
}
不幸的是,调用永远不会返回,查询将无限挂起

原因可能是什么,因为坦白说我不知道从哪里开始

不幸的是,调用从未返回,查询无限期挂起

你有一个典型的僵局。这就是为什么。在幕后,编译器生成一个状态机并捕获一个称为
SynchronizationContext
的东西。当同步阻止调用线程时,尝试将延续发回同一上下文会导致死锁

不要与
.Result
同步阻塞,而是使控制器异步并等待从
GetSecret
返回的
任务:

public async IHttpActionResult FooAsync()
{
    var secret = await KeyVaultAccessor.GetSecretAsync("https://superSecretUri");
    return Ok();
}

旁注-异步方法应遵循命名约定,并使用
Async

进行后期修复这是我所遇到的常见死锁问题。简而言之,
async
方法试图在
wait
完成后返回ASP.NET请求上下文,但该请求一次只允许一个线程,并且该上下文中已经有一个线程(调用
Result
时被阻止的线程)。因此,任务正在等待上下文释放,线程正在阻塞上下文,直到任务完成:死锁

正确的解决方案是使用
等待
而不是
结果

var secret = await KeyVaultAccessor.GetSecret("https://superSecretUri");

我已使用以下代码覆盖同步上下文:

var secret = Task.Run(async () => await KeyVaultAccessor.GetSecretAsync("https://superSecretUri")).Result;
这仍然允许您使用
.Result
如果您使用的是非异步方法

请使用RESTAPI

public class AzureKeyVaultClient
{


    public string GetSecret(string name, string vault)
    {
        var client = new RestClient($"https://{vault}.vault.azure.net/");
        client.Authenticator = new AzureAuthenticator($"https://vault.azure.net");
        var request = new RestRequest($"secrets/{name}?api-version=2016-10-01");

        request.Method = Method.GET;


        var result = client.Execute(request);

        if (result.StatusCode != HttpStatusCode.OK)
        {
            Trace.TraceInformation($"Unable to retrieve {name} from {vault} with response {result.Content}");
            var exception =  GetKeyVaultErrorFromResponse(result.Content);
            throw exception;

        }
        else
        {
            return GetValueFromResponse(result.Content);
        }




    }

    public string GetValueFromResponse(string content)
    {

            var result = content.FromJson<keyvaultresponse>();
            return result.value;

    }


    public Exception GetKeyVaultErrorFromResponse(string content)
    {
        try
        {

            var result = content.FromJson<keyvautlerrorresponse>();
            var exception = new Exception($"{result.error.code} {result.error.message}");
            if(result.error.innererror!=null)
            {
                var innerException = new Exception($"{result.error.innererror.code} {result.error.innererror.message}");
            }

            return exception;
        }
        catch(Exception e)
        {
            return e;
        }
    }

    class keyvaultresponse
    {
        public string value { get; set; }
        public string contentType { get; set; }

    }

    class keyvautlerrorresponse
    {
        public keyvaulterror error {get;set;}
    }

    class keyvaulterror
    {
        public string code { get; set; }

        public string message { get; set; }

        public keyvaulterror innererror { get; set; }
    }

    class AzureAuthenticator : IAuthenticator
    {

        private string _authority;
        private string _clientId;
        private string _clientSecret;
        private string _resource;



        public AzureAuthenticator(string resource)
        {
            _authority = WebConfigurationManager.AppSettings["azure:Authority"];
            _clientId = WebConfigurationManager.AppSettings["azure:ClientId"];
            _clientSecret = WebConfigurationManager.AppSettings["azure:ClientSecret"];
            _resource = resource;

        }

        public AzureAuthenticator(string resource, string tennant, string clientid, string secret)
        {
            //https://login.microsoftonline.com/<tennant>/oauth2/oken
            _authority = authority;
            //azure client id (web app or native app
            _clientId = clientid;
            //azure client secret
            _clientSecret = secret;
            //vault.azure.net
            _resource = resource;

        }


        public void Authenticate(IRestClient client, IRestRequest request)
        {

            var token = GetS2SAccessTokenForProdMSA().AccessToken;
            //Trace.WriteLine(String.Format("obtained bearer token {0} from ADAL and adding to rest request",token));
            request.AddHeader("Authorization", String.Format("Bearer {0}", token));


        }

        public AuthenticationResult GetS2SAccessTokenForProdMSA()
        {
            return GetS2SAccessToken(_authority, _resource, _clientId, _clientSecret);
        }

        private AuthenticationResult GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
        {
            var clientCredential = new ClientCredential(clientId, clientSecret);
            AuthenticationContext context = new AuthenticationContext(authority, false);
            AuthenticationResult authenticationResult = context.AcquireToken(
                resource,
                clientCredential);
            return authenticationResult;
        }

    }
}
公共类AzureKeyVault客户端
{
公共字符串GetSecret(字符串名称、字符串库)
{
var client=new RestClient($“https://{vault}.vault.azure.net/”);
client.Authenticator=新AzureAuthenticator($”https://vault.azure.net");
var请求=新的重新请求($“secrets/{name}?api version=2016-10-01”);
request.Method=Method.GET;
var result=client.Execute(请求);
if(result.StatusCode!=HttpStatusCode.OK)
{
Trace.TraceInformation($“无法使用响应{result.Content}从{vault}检索{name}”);
var exception=GetKeyVaultErrorFromResponse(result.Content);
抛出异常;
}
其他的
{
返回GetValueFromResponse(result.Content);
}
}
公共字符串GetValueFromResponse(字符串内容)
{
var result=content.FromJson();
返回result.value;
}
公共异常GetKeyVaultErrorFromResponse(字符串内容)
{
尝试
{
var result=content.FromJson();
var exception=新异常($“{result.error.code}{result.error.message}”);
if(result.error.innererror!=null)
{
var innerException=新异常($“{result.error.innererror.code}{result.error.innererror.message}”);
}
返回异常;
}
捕获(例外e)
{
返回e;
}
}
类键响应
{
公共字符串值{get;set;}
公共字符串contentType{get;set;}
}
类keyvautlerrorresponse
{
public KeyVault错误{get;set;}
}
类keyvaulterror
{
公共字符串代码{get;set;}
公共字符串消息{get;set;}
public keyvaulterror innererror{get;set;}
}
AzureAuthenticator类:IAAuthenticator
{
私人字符串管理机构;
私有字符串_clientId;
私有字符串_clientSecret;
私有字符串资源;
公共AzureAuthenticator(字符串资源)
{
_authority=WebConfiguration Manager.AppSettings[“azure:authority”];
_clientId=WebConfiguration Manager.AppSettings[“azure:clientId”];
_clientSecret=WebConfiguration Manager.AppSettings[“azure:clientSecret”];
_资源=资源;
}
公共AzureAuthenticator(字符串资源、字符串tennant、字符串clientid、字符串机密)
{
//https://login.microsoftonline.com//oauth2/oken
_权威=权威;
//azure客户端id(web应用程序或本机应用程序)
_clientId=clientId;
//azure客户端机密
_clientSecret=秘密;
//vault.azure.net
_资源=资源;
}
公共无效身份验证(IRestClient客户端、IRestRequest请求)
{
var token=GetS2SAccessTokenForProdMSA().AccessToken;
//WriteLine(String.Format(“从ADAL获得的承载令牌{0}并添加到rest请求中”,token));
AddHeader(“Authorization”,String.Format(“Bearer{0}”,token));
}
公共身份验证结果GetS2SAccessTokenForProdMSA()
{
返回GetS2SAccessToken(_authority,_resource,_clientId,_clientSecret);
}
私有身份验证结果GetS2SAccessToken(字符串权限、字符串资源、字符串clientId、字符串clientSecret)
{
var clientCredential=新的clientCredential(clientId,clientSecret);
AuthenticationContext=新的AuthenticationContext(authority,false);
AuthenticationResult AuthenticationResult=context.AcquireToken(
资源,,
客户凭证);
返回authenticationResult;
}
}
}

此通用方法可用于覆盖死锁问题:

public static T SafeAwaitResult<T>(Func<Task<T>> f)
{
    return Task.Run(async () => await f()).Result;
}

因此,如果我有一个不使用异步方法的现有应用程序,我需要进行一些重构才能使用这个库?@GabrielG.Roy:是的,这是最好的方法。有没有一种快速而肮脏的方法可以让我同步地进行这些调用?@GabrielG.Roy:没有。没有
public static T SafeAwaitResult<T>(Func<Task<T>> f)
{
    return Task.Run(async () => await f()).Result;
}
SafeAwaitResult(() => foo(param1, param2));