对 Web api 的 ASP.NET Core 应用程序 http 客户端请求

Posted

技术标签:

【中文标题】对 Web api 的 ASP.NET Core 应用程序 http 客户端请求【英文标题】:ASP.NET Core application http client request to a web api 【发布时间】: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错误,不知道为什么? 【参考方案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;
                
            ;
        );
    

【讨论】:

以上是关于对 Web api 的 ASP.NET Core 应用程序 http 客户端请求的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core Web API InvalidOperationException:无法解析服务 [重复]

ASP.NET Core Web API + Angular 对预检请求的响应未通过访问控制检查:预检请求不允许重定向

ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

ASP.NET Core 1 Web API 模型绑定数组

ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

Xamarin Mobile 使用的 ASP.Net Core Web API