Dapatkan alamat IP dari host jarak jauh

138

Di ASP.NET ada System.Web.HttpRequestkelas, yang berisi ServerVariablesproperti yang dapat memberi kita alamat IP dari REMOTE_ADDRnilai properti.

Namun, saya tidak dapat menemukan cara serupa untuk mendapatkan alamat IP dari host jarak jauh dari ASP.NET Web API.

Bagaimana saya bisa mendapatkan alamat IP dari host jarak jauh yang membuat permintaan?

paulius_l
sumber

Jawaban:

191

Itu mungkin dilakukan, tetapi tidak terlalu bisa ditemukan - Anda perlu menggunakan tas properti dari permintaan yang masuk, dan properti yang perlu Anda akses bergantung pada apakah Anda menggunakan API Web di bawah IIS (dihosting web) atau dihosting sendiri. Kode di bawah ini menunjukkan bagaimana ini bisa dilakukan.

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;
}
carlosfigueira.dll
sumber
4
Terima kasih, saya juga mencari ini. Perbaikan kecil = kelas ekstensi: gist.github.com/2653453
MikeJansen
29
WebAPI sebagian besar sangat bersih. Sayang sekali kode seperti ini diperlukan untuk sesuatu yang sepele seperti IP.
Toad
2
Apakah RemoteEndpointMessagePropertykelas di System.ServiceModel.Channelsnamespace, System.ServiceModel.dllassembly? Bukankah itu majelis milik WCF?
Slauma
4
@Slauma, ya, mereka. ASP.NET Web API (saat ini) diimplementasikan dalam dua "rasa", self-hosted dan web-hosted. Versi yang dihosting web diimplementasikan di atas ASP.NET, sedangkan versi yang dihosting sendiri di atas pendengar WCF. Perhatikan bahwa platform (ASP.NET Web API) itu sendiri adalah hosting-agnostik, jadi mungkin saja seseorang akan menerapkan hosting yang berbeda di masa mendatang dan host akan menampilkan properti tersebut (titik akhir jarak jauh) secara berbeda.
carlosfigueira
3
Sayangnya, ini tidak berfungsi jika Anda menghosting sendiri menggunakan Owin (seperti yang disarankan untuk Web API 2). Butuh yang lain jika ada ...
Nikolai Samteladze
75

Solusi ini juga mencakup API Web yang dihosting sendiri menggunakan Owin. Sebagian dari sini .

Anda dapat membuat metode privat dalam diri Anda ApiControlleryang akan mengembalikan alamat IP jarak jauh tidak peduli bagaimana Anda menghosting API Web Anda:

 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;
 }

Referensi diperlukan:

  • HttpContextWrapper - System.Web.dll
  • RemoteEndpointMessageProperty - System.ServiceModel.dll
  • OwinContext - Microsoft.Owin.dll (Anda akan memilikinya jika Anda menggunakan paket Owin)

Masalah kecil dengan solusi ini adalah Anda harus memuat pustaka untuk semua 3 kasus ketika Anda sebenarnya hanya akan menggunakan salah satu dari mereka selama runtime. Seperti yang disarankan di sini , ini dapat diatasi dengan menggunakan dynamicvariabel. Anda juga dapat menulis GetClientIpAddressmetode sebagai ekstensi 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;
    }
}

Sekarang Anda bisa menggunakannya seperti ini:

public class TestController : ApiController
{
    [HttpPost]
    [ActionName("TestRemoteIp")]
    public string TestRemoteIp()
    {
        return Request.GetClientIpAddress();
    }
}
Nikolai Samteladze
sumber
1
Solusi ini harus menggunakan namespace "System.Net.Http" ini untuk bekerja. Karena ini adalah nama kelas di Assembly System.Web.Http.dll, v5.2.2.0.
Wagner Bertolini Junior
@WagnerBertolini, Anda benar, Anda perlu using System.Net.Http;baris karena Anda memperluas HttpRequestMessage. Kecuali Anda mendefinisikan ekstensi Anda di System.Net.Httpnamespace yang sangat dipertanyakan. Tidak yakin itu penting, karena itu akan ditambahkan secara otomatis oleh IDE atau alat produktivitas apa pun. Bagaimana menurut anda?
Nikolai Samteladze
Saya terburu-buru mencoba menyelesaikan satu pekerjaan di sini dan saya membutuhkan lebih dari 20 menit untuk melihat apa yang terjadi pada kesalahan pembuatan. Saya hanya menyalin kode dan membuat kelas untuk itu, ketika mengkompilasinya tidak menunjukkan metodenya, ketika saya menggunakan "pergi ke definisi" di VS itu membawa saya ke kelas saya, dan saya tetap tidak mengerti apa yang terjadi, sampai saya menemukan kelas lain. Ekstensi adalah fitur yang cukup baru dan tidak digunakan sepanjang waktu, karena, menurut saya, menghemat waktu ini adalah ide yang bagus.
Wagner Bertolini Junior
1
Saat menggunakan OWIN, Anda cukup menggunakan OwinHttpRequestMessageExtensions untuk mendapatkan konteks OWIN seperti: request.GetOwinContext (). Request.RemoteIpAddress
Stef Heyenrath
1
Ini sebenarnya harus var ctx = request.Properties [MsHttpContext] sebagai HttpContextWrapper; E Jika Anda melakukan cast, Anda tidak perlu memeriksa null karena jika cast gagal, Anda mendapatkan pengecualian
Stef Heyenrath
33

Jika Anda benar-benar menginginkan one-liner dan tidak berencana untuk menghosting API Web sendiri:

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

Jawaban di atas memerlukan referensi ke System.Web agar dapat mentransmisikan properti ke HttpContext atau HttpContextWrapper. Jika Anda tidak menginginkan referensi, Anda bisa mendapatkan ip menggunakan dinamika:

var host = ((dynamic)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
Robin van der Knaap
sumber
-2

Solusi yang disediakan oleh carlosfigueira berfungsi, tetapi satu baris yang aman untuk tipe lebih baik: Tambahkan using System.Webkemudian akses HttpContext.Current.Request.UserHostAddresske metode tindakan Anda.

Warren Rumak
sumber
26
-1 ini tidak dapat dipercaya dalam api web karena tidak disimpan HttpContext.Currentdengan benar di seluruh alur tugas; karena semua penanganan permintaan bersifat asinkron. HttpContext.Currentharus hampir selalu dihindari saat menulis kode API Web.
Andras Zoltan
@ Andras, saya ingin tahu lebih detail mengapa menggunakan HttpContext.Current itu buruk, apakah Anda tahu sumber daya yang berharga untuk ini?
cuongle
13
Hai @cuonge; pada kenyataannya - sementara masalah threading bisa menjadi masalah (meskipun tidak selalu langsung jika SynchronizationContextdialirkan dengan benar antar tugas); Masalah terbesar dengan ini adalah jika kode layanan Anda kemungkinan besar akan dihosting sendiri (pengujian, misalnya) - HttpContext.Currentmerupakan konstruksi Asp.Net murni dan tidak ada saat Anda menghosting sendiri.
Andras Zoltan
Keamanan tipe bukanlah segalanya. Kode ini akan menampilkan hard-to-debug NullReferenceExceptionjika digunakan dari utas (mis. Tugas awaiter, sangat umum dalam kode API Web modern) atau dalam konteks yang dihosting sendiri. Setidaknya sebagian besar jawaban lain akan kembali null.
Aaronaught