【问题标题】:Call Web API from MVC Controller using the same HttpClient使用相同的 HttpClient 从 MVC 控制器调用 Web API
【发布时间】:2016-05-12 04:56:52
【问题描述】:

我有一个 MVC5 项目,我需要从 MVC 控制器调用 Web API 方法。 Web API 使用基于令牌的身份验证,因此我必须为每次调用传递令牌。我正在使用下面的代码在 HTTP 标头中传递令牌:

HttpClient httpClient = new HttpClient();
string baseUrl = "http://localhost:60477/";
dynamic token = Session["token"];
if (token.AccessToken != null)
{
    httpClient.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", token.AccessToken));
}

我的控制器中有多个操作方法,我想使用单个 HttpClient 和标头,添加到一个位置,而不是在每个操作方法中添加标头。

HttpClient 标头注册码在 MVC 应用程序中哪里可以放置,以便所有控制器都可以通用?这意味着我不想重复代码,例如在每个操作方法中添加标记。我该怎么做?

Public ActionResult Postuser(UserModel user)
{
    // post code
}

Public ActionResult getuser(UserModel user)
{
    HttpResponseMessage response = httpClient.GetAsync(baseUrl + "api/Admin/GetStates").Result;
    if (response.IsSuccessStatusCode)
    {
        string stateInfo = response.Content.ReadAsStringAsync().Result;
    }

}

Public ActionResult PostRoles(RoleModel role)
{
    // post roles code
}

【问题讨论】:

    标签: c# asp.net-mvc asp.net-web-api asp.net-mvc-5


    【解决方案1】:

    您可以尝试创建一个小的帮助程序类来创建您的httpclient 对象。类似的东西

    public class HttpClientHelper
    {
        public static HttpClient GetHttpClient()
        {
            var MyHttpClient = new HttpClient();
            dynamic _token = HttpContext.Current.Session["token"];
            if (_token == null) throw new ArgumentNullException(nameof(_token));
            MyHttpClient.DefaultRequestHeaders.Add("Authorization", String.Format("Bearer {0}", _token.AccessToken));
            return MyHttpClient;
        }
    }
    

    然后在你的控制器中调用它

    public ActionResult getuser(UserModel user)
    {
        var httpClient = HttpClientHelper.GetHttpClient();
        HttpResponseMessage response = httpClient.GetAsync(baseUrl + "api/Admin/GetStates").Result;
        if (response.IsSuccessStatusCode)
        {
            string stateInfo = response.Content.ReadAsStringAsync().Result;
        }
    }
    

    【讨论】:

      【解决方案2】:

      最好遵守单一职责原则,并在它自己的类中提取与另一个服务的交互,例如

      public class ServiceClient : IServiceClient
      {
          private HttpClient m_Client;        
      
          public ServiceClient
          {
               m_Client = new HttpClient();
               // Initialize the client as you need here
          }
      
          public void CallSomeMethod()
          {
              // Call method on the client
          }
      }
      

      然后您将 IServiceClient 注入您的控制器并调用它的方法。如果你不使用注入(我建议你这样做),你可以在控制器的构造函数中创建一个新实例。

      【讨论】:

        【解决方案3】:

        您可以尝试在控制器中使用操作过滤器。尝试添加一个看起来像这样的覆盖-

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
          // some condition code to target a specific method in the controller
          // Example
          if (filterContext.ActionDescriptor.ActionName == "getuser") // <-- your method
          {
            // put your token based authentication code here
          }
        
          base.OnActionExecuting(filterContext);
        }
        

        OnActionExecuting 方法在控制器范围内,因此您可以为不同的控制器设置不同的逻辑。

        如果您想在您的操作方法之后运行代码,还有一个 OnActionExecuted 方法覆盖。

        ------编辑--------------

        至于把你的HttpClient代码sn-p放在哪里,你可以试试这个-

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
          HttpClient httpClient = new HttpClient();
          string baseUrl = "http://localhost:60477/";
          dynamic token = Session["token"];
        
          if (token.AccessToken != null)
          {
            httpClient.DefaultRequestHeaders.Add(
                "Authorization",
                string.Format("Bearer {0}", token.AccessToken)
            );
        
            httpClient.BaseAddress = new Uri(baseUrl);
          }
        
          if(filterContext.ActionParameters.ContainsKey("httpClient"))
          {
            filterContext.ActionParameters["httpClient"] = httpClient;
          }
          else
          {
            // error
          }
        
          base.OnActionExecuting(filterContext);
        }
        

        因此,HttpClient 对象以及您的 baseUrl 的分配是在 OnActionExecuting 中建立的。此代码将在您正在重构的控制器中返回 ActionResult 的任何方法之前运行。如果您想针对某些而不是所有方法,请参阅上面的第一个 OnActionExecuting 示例。

        public ActionResult getuser(UserModel user, HttpClient httpClient)
        {
          HttpResponseMessage response = httpClient.GetAsync("api/Admin/GetStates").Result;
        
          if(response.IsSuccessStatusCode)
          {
            string stateInfo = response.Content.ReadAsStringAsync().Result;
          }
        
          // the rest of your code for getuser..
        
          return View();
        }
        

        现在您的 getuser 方法有一个额外的参数( HttpClient httpClient )。

        【讨论】:

        • 查看我的帖子的编辑。此外,像 Ilya Chernomordik 建议的那样注入服务是理想的,正如 Danny 建议使用辅助方法一样。我的建议可能不是最好的,但如果你想探索动作过滤器,那就试试吧。添加到您的方法中的 HttpClient 参数可以重构为服务对象,以便将 OnActionExecuting 覆盖方法中的代码移入服务对象中进行注入。希望有帮助!
        【解决方案4】:

        为什么不移动 Global asax 中的代码或创建自定义属性?

        这是一个很好的链接: http://www.diaryofaninja.com/blog/2011/07/24/writing-your-own-custom-aspnet-mvc-authorize-attributes

        【讨论】:

        • 谢谢。如何将 httpclient 代码放在 global.asax 文件中的任何示例?
        猜你喜欢
        • 1970-01-01
        • 2018-06-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多