Lấy địa chỉ IP của máy chủ từ xa


136

Trong ASP.NET có một System.Web.HttpRequestlớp chứa thuộc ServerVariablestính có thể cung cấp cho chúng ta địa chỉ IP từ REMOTE_ADDRgiá trị thuộc tính.

Tuy nhiên, tôi không thể tìm thấy một cách tương tự để lấy địa chỉ IP của máy chủ từ xa từ API Web ASP.NET.

Làm cách nào tôi có thể nhận địa chỉ IP của máy chủ từ xa đang thực hiện yêu cầu?

Câu trả lời:


189

Bạn có thể làm điều đó, nhưng không thể khám phá được - bạn cần sử dụng túi thuộc tính từ yêu cầu đến và thuộc tính bạn cần truy cập tùy thuộc vào việc bạn đang sử dụng API Web trong IIS (webhosted) hay tự lưu trữ. Mã dưới đây cho thấy làm thế nào điều này có thể được thực hiện.

private string GetClientIp(HttpRequestMessage request)
{
    if (request.Properties.ContainsKey("MS_HttpContext"))
    {
        return ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
    }

    if (request.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
    {
        RemoteEndpointMessageProperty prop;
        prop = (RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessageProperty.Name];
        return prop.Address;
    }

    return null;
}

4
Cảm ơn, tôi đã tìm kiếm điều này quá. Cải thiện nhỏ = lớp mở rộng: gist.github.com/2653453
MikeJansen

28
WebAPI là phần rất sạch sẽ. Thật xấu hổ khi mã như thế này là cần thiết cho một cái gì đó tầm thường như một IP.
cóc

2
RemoteEndpointMessagePropertylớp trong System.ServiceModel.Channelskhông gian tên, System.ServiceModel.dlllắp ráp? Không phải đó là một hội đồng thuộc về WCF?
Slauma

4
@Slauma, vâng, họ có. API Web ASP.NET (hiện tại) được triển khai theo hai "hương vị", tự lưu trữ và lưu trữ web. Phiên bản lưu trữ web được triển khai trên ASP.NET, trong khi phiên bản tự lưu trữ trên đầu trình nghe WCF. Lưu ý rằng chính nền tảng (ASP.NET Web API) là không lưu trữ, vì vậy có thể ai đó sẽ triển khai một lưu trữ khác trong tương lai và máy chủ sẽ hiển thị thuộc tính đó (điểm cuối từ xa) khác nhau.
carlosfigueira

3
Thật không may, điều này không hoạt động nếu bạn tự lưu trữ bằng Owin (vì nó được khuyến nghị cho API Web 2). Cần cái khác nếu có ...
Nikolai Samteladze

74

Giải pháp này cũng bao gồm API Web tự lưu trữ bằng Owin. Một phần từ đây .

Bạn có thể tạo một phương thức riêng tư trong ApiControllerđó sẽ trả về địa chỉ IP từ xa cho dù bạn lưu trữ API Web như thế nào:

 private const string HttpContext = "MS_HttpContext";
 private const string RemoteEndpointMessage =
     "System.ServiceModel.Channels.RemoteEndpointMessageProperty";
 private const string OwinContext = "MS_OwinContext";

 private string GetClientIp(HttpRequestMessage request)
 {
       // Web-hosting
       if (request.Properties.ContainsKey(HttpContext ))
       {
            HttpContextWrapper ctx = 
                (HttpContextWrapper)request.Properties[HttpContext];
            if (ctx != null)
            {
                return ctx.Request.UserHostAddress;
            }
       }

       // Self-hosting
       if (request.Properties.ContainsKey(RemoteEndpointMessage))
       {
            RemoteEndpointMessageProperty remoteEndpoint =
                (RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessage];
            if (remoteEndpoint != null)
            {
                return remoteEndpoint.Address;
            }
        }

       // Self-hosting using Owin
       if (request.Properties.ContainsKey(OwinContext))
       {
           OwinContext owinContext = (OwinContext)request.Properties[OwinContext];
           if (owinContext != null)
           {
               return owinContext.Request.RemoteIpAddress;
           }
       }

        return null;
 }

Yêu cầu tham khảo:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (bạn sẽ có sẵn nếu bạn sử dụng gói Owin)

Một vấn đề nhỏ với giải pháp này là bạn phải tải thư viện cho cả 3 trường hợp khi bạn thực sự sẽ chỉ sử dụng một trong số chúng trong thời gian chạy. Như được đề xuất ở đây , điều này có thể được khắc phục bằng cách sử dụng dynamiccác biến. Bạn cũng có thể viết GetClientIpAddressphương thức như một phần mở rộng cho HttpRequestMethod.

using System.Net.Http;

public static class HttpRequestMessageExtensions
{
    private const string HttpContext = "MS_HttpContext";
    private const string RemoteEndpointMessage =
        "System.ServiceModel.Channels.RemoteEndpointMessageProperty";
    private const string OwinContext = "MS_OwinContext";

    public static string GetClientIpAddress(this HttpRequestMessage request)
    {
       // Web-hosting. Needs reference to System.Web.dll
       if (request.Properties.ContainsKey(HttpContext))
       {
           dynamic ctx = request.Properties[HttpContext];
           if (ctx != null)
           {
               return ctx.Request.UserHostAddress;
           }
       }

       // Self-hosting. Needs reference to System.ServiceModel.dll. 
       if (request.Properties.ContainsKey(RemoteEndpointMessage))
       {
            dynamic remoteEndpoint = request.Properties[RemoteEndpointMessage];
            if (remoteEndpoint != null)
            {
                return remoteEndpoint.Address;
            }
        }

       // Self-hosting using Owin. Needs reference to Microsoft.Owin.dll. 
       if (request.Properties.ContainsKey(OwinContext))
       {
           dynamic owinContext = request.Properties[OwinContext];
           if (owinContext != null)
           {
               return owinContext.Request.RemoteIpAddress;
           }
       }

        return null;
    }
}

Bây giờ bạn có thể sử dụng nó như thế này:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}

1
Giải pháp này nên sử dụng không gian tên "System.Net.Http" này để hoạt động. Vì đây là tên lớp trên hội System.Web.Http.dll, v5.2.2.0.
Wagner Bertolini Junior

@WagnerBertolini, bạn là chính xác, bạn cần using System.Net.Http;dòng vì bạn đang mở rộng HttpRequestMessage. Trừ khi bạn đang xác định tiện ích mở rộng của mình trong System.Net.Httpkhông gian tên rất đáng nghi ngờ. Tuy nhiên, không chắc chắn rằng đó là điều cần thiết, bởi vì nó sẽ được tự động thêm bởi bất kỳ IDE hoặc công cụ năng suất nào. Bạn nghĩ sao?
Nikolai Samteladze

Tôi đã vội vàng cố gắng hoàn thành một công việc ở đây và tôi mất hơn 20 phút để xem lỗi gì đang xảy ra. Tôi vừa sao chép mã và tạo một lớp cho nó, khi biên dịch nó không hiển thị các phương thức, khi tôi sử dụng "đi định nghĩa" trên VS, nó đưa tôi đến lớp của tôi và tôi không hiểu chuyện gì đang xảy ra, cho đến khi tôi tìm thấy lớp khác. Tiện ích mở rộng là một tính năng khá mới và không được sử dụng mọi lúc, vì tôi nghĩ rằng sẽ tiết kiệm được thời gian này.
Wagner Bertolini Junior

1
Khi sử dụng OWIN, bạn chỉ có thể sử dụng OwinHttpRequestMessageExtensions để có được bối cảnh OWIN như: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath 7/07/2016

1
Nó thực sự phải là var ctx = request.Properies [MsHttpContext] với tên httpContextWrapper; Nếu bạn chọn, bạn không cần phải kiểm tra null vì nếu diễn viên thất bại, bạn sẽ có một ngoại lệ
Stef Heyenrath

31

Nếu bạn thực sự muốn có một lớp lót và không có kế hoạch tự lưu trữ API Web:

((System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"]).Request.UserHostAddress;

13

Các câu trả lời ở trên yêu cầu tham chiếu đến System.Web để có thể truyền thuộc tính sang HttpContext hoặc HttpContextWrapper. Nếu bạn không muốn tham chiếu, bạn có thể lấy ip bằng cách sử dụng động:

var host = ((dynamic)request.Properties["MS_HttpContext"]).Request.UserHostAddress;

-1

Giải pháp được cung cấp bởi carlosfigueira hoạt động, nhưng loại một lớp an toàn tốt hơn: Thêm một using System.Webtruy cập sau đó HttpContext.Current.Request.UserHostAddresstrong phương thức hành động của bạn.


26
-1 điều này không thể tin cậy trong api web vì HttpContext.Currentkhông được duy trì chính xác trong suốt quá trình thực hiện nhiệm vụ; vì tất cả xử lý yêu cầu là không đồng bộ. HttpContext.Currenthầu như luôn luôn phải tránh khi viết mã API Web.
Andras Zoltan

@Andras, tôi muốn biết thêm chi tiết tại sao sử dụng HttpContext.C hiện tại là xấu, bạn có biết bất kỳ tài nguyên có giá trị nào cho việc này không?
cuongle

13
Xin chào @CuongLe; trong thực tế - trong khi vấn đề luồng có thể là một vấn đề (mặc dù không phải lúc nào cũng trực tiếp nếu luồng được chuyển SynchronizationContextchính xác giữa các tác vụ); vấn đề lớn nhất với điều này là nếu mã dịch vụ của bạn có khả năng tự lưu trữ (ví dụ thử nghiệm) - HttpContext.Currenthoàn toàn là cấu trúc Asp.Net và không tồn tại khi bạn tự lưu trữ.
Andras Zoltan

Loại an toàn không phải là tất cả. Mã này sẽ đưa ra một bản sửa lỗi khó NullReferenceExceptionsử dụng nếu được sử dụng từ một luồng (ví dụ: trình chờ đợi tác vụ, rất phổ biến trong mã API Web hiện đại) hoặc trong ngữ cảnh tự lưu trữ. Ít nhất là hầu hết các câu trả lời khác sẽ chỉ trở lại null.
Aaronaught
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.