【问题标题】:ASP.NET Core application http client request to a web api对 Web api 的 ASP.NET Core 应用程序 http 客户端请求
【发布时间】:2021-12-23 07:52:49
【问题描述】:

我有一个正在运行的 Web API,我尝试从中获取不记名令牌。从 Postman 开始的请求正在工作,我取回了令牌。从我的应用程序执行后,我总是会收到 http 400 Bad Request 错误。

我在这里错过了什么?

public async Task<string> GetToken(string userName, string passWord)
{
    var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");

    request.Headers.Authorization = new AuthenticationHeaderValue(
                "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
    request.Headers.Host = "api.my-host.com";
    request.Headers.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
            
    var response = await _httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode();

    using var responseStream = await response.Content.ReadAsStreamAsync();

    var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);

    return authResult == null ? "" : authResult.Access_Token;
}

根据要求,这里是 Postman 结果的屏幕截图:

我创建了一个 HttpGet 请求,并在代码中添加了来自 Postman 的不记名令牌,然后我收到了数据。只是令牌请求好像有问题。

还有我的控制器:

namespace AmsAPI.Controller
{

    [Produces("application/json")]
    [Route("api/auth")]
    [ApiController]
    [Authorize]
    public class AuthenticationController : ControllerBase
    {
        private readonly IAuthenticationManager _authenticationManager;

        public AuthenticationController(IAuthenticationManager authenticationManager)
        {
            _authenticationManager = authenticationManager;
        }

        [HttpPost("login"), AllowAnonymous]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesDefaultResponseType]
        public async Task<ActionResult> Login([FromHeader] byte[] basic)
        {
            if (!ModelState.IsValid) return BadRequest();

            string Basic = Encoding.UTF8.GetString(basic);

            var splitBasic = Basic.Split(':');

            AuthCredentials credentials = new()
            {
                UserName = splitBasic[0],
                Password = splitBasic[1]
            };

            return await _authenticationManager.SignInCheck(credentials) ?
                Ok(new
                {
                    message = string.Format("User {0} successfully logged in.", credentials.UserName),
                    access_token = await _authenticationManager.CreateToken(),
                    token_type = "bearer",
                    expires_in = "3600"
                }) :
                Unauthorized();
        }

        [HttpGet("user")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesDefaultResponseType]
        public async Task<List<User>> GetUser() => await _authenticationManager.GetUser();
    }
}

【问题讨论】:

  • 请尝试通过邮递员请求令牌,并将结果显示给我们。我们需要检查这个http请求的内容吗?
  • 共享您的完整控制器?您的控制器是否允许annonymous 请求?令牌控制器应该允许匿名请求。
  • 那么 Postman 也无法工作...控制器有 annonymous 请求
  • 您能否添加完整的控制器类,但仍不清楚问题所在
  • 这是完整的控制器。不知道你在找什么? Postman从控制器收到正确的数据,但是客户端总是收到Bad Request错误,不知道为什么?

标签: asp.net-core http-headers dotnet-httpclient webapi bearer-token


【解决方案1】:

嗯,我已经成功重现了你的问题。

您收到 400 是因为它正在搜索 SSL 凭据,但是 默认情况下,我们的请求没有证书绑定,它没有 必需的。所以要处理这个400 异常,你必须使用 HttpClientHandler 将绕过证书错误。这样你就可以 尝试以下方式以成功获取令牌响应。

HTTP Request:

public async Task<string> GetToken()
        {

            var user = new UserCred();
            user.user_name = "admin";
            user.password = "123456";
            var handler = new HttpClientHandler();
            handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };

            var client = new HttpClient(handler);
            var json = JsonConvert.SerializeObject(user);

            var data = new StringContent(json, Encoding.UTF8, "application/json");
            var url = "http://localhost:21331/api/Authentication/login";
            var response = await client.PostAsync(url, data);

            string result = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(result);
            return result;
            
        }

Output:

Note: 您需要添加以下代码 sn-p 来解决您的问题。但我喜欢以上述方式调用HTTP POST请求以减少它 干净的。您可以继续使用您的代码,只需调整我的建议即可。

        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
        var client = new HttpClient(handler);

希望以上步骤能相应解决您的问题。

【讨论】:

    【解决方案2】:

    Postman 似乎将基本授权标头作为 Http 请求的内容而不是在标头中发送,但我的 Web 应用程序在标头中正确实现了授权。 所以在 WebApi 中它看起来像

        [Authorize]
        [Route("api/auth")]
        [ApiController]
        public class AuthenticationController : ControllerBase
        {
            private readonly IAuthenticationManager _authenticationManager;
    
            public AuthenticationController(IAuthenticationManager authenticationManager)
            {
                _authenticationManager = authenticationManager;
            }
    
            [HttpPost("login"), AllowAnonymous]
            [ProducesResponseType(StatusCodes.Status200OK)]
            [ProducesDefaultResponseType]
            public async Task<ActionResult> Login()
            {
                //check if header has Authorization
                if (!Request.Headers.ContainsKey("Authorization")) return BadRequest();
    
                try
                {
                    AuthenticationHeaderValue authenticationHeaderValue = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
    
                    if (authenticationHeaderValue == null) throw new Exception();
    
                    var bytes = Convert.FromBase64String(authenticationHeaderValue.Parameter ?? throw new Exception());
    
                    string[] creds = Encoding.UTF8.GetString(bytes).Split(':');
    
                    AuthCredentials credentials = new()
                    {
                        UserName = creds[0],
                        Password = creds[1]
                    };
    
                    return await _authenticationManager.SignInCheck(credentials) ?
                        Ok(new
                        {
                            message = string.Format("User {0} successfully logged in.", credentials.UserName),
                            access_token = await _authenticationManager.CreateToken(),
                            token_type = "bearer",
                            expires_in = "3600"
                        }) :
                        Unauthorized();
                }
                catch (Exception)
                {
                    return BadRequest();
                }
            }
        }
    

    我的要求如下:

     public async Task<string> GetToken(string userName, string passWord)
        {
            //set request
            var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");
            //set Header
            request.Headers.Authorization = new AuthenticationHeaderValue(
                "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
            //get response
            var response = await _httpClient.SendAsync(request);
    
            response.EnsureSuccessStatusCode();
    
            using var responseStream = await response.Content.ReadAsStreamAsync();
    
            var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);
    
            var token = authResult.Access_Token;
    
            return authResult == null ? "Authorization failed!" : "Bearer token successfully created!";
        }
    

    并且httpClient被外包到一个Service中

    public static void ConfigureServices(this IServiceCollection services)
        {
            var url = Environment.GetEnvironmentVariable("amsApiUrl");
            var host = Environment.GetEnvironmentVariable("amsHostUrl");
            //set HttpClient
            services.AddHttpClient<IAmsAccountService, AmsAccountService>(c =>
            {
                c.BaseAddress = new Uri(url ?? "");
                c.DefaultRequestHeaders.Accept.Clear();
                c.DefaultRequestHeaders.Host = host;
                c.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
                c.Timeout = TimeSpan.FromSeconds(10);
            })
            .ConfigurePrimaryHttpMessageHandler(() =>
            {
                return new HttpClientHandler()
                {
                    CookieContainer = new CookieContainer(),
                    ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
                    {
                        return true;
                    }
                };
            });
        }
    

    【讨论】:

      猜你喜欢
      • 2017-07-10
      • 2013-07-03
      • 2018-11-30
      • 2022-01-08
      • 2016-09-25
      • 1970-01-01
      • 2020-08-31
      • 2012-08-15
      • 2017-10-30
      相关资源
      最近更新 更多