I am implementing some code where I use a visitors IP address to determine their location. For .net core 2, this is:
var ipAddress = Request.HttpContext.Connection.RemoteIpAddress;
But of course when I test locally I always get the loop back address ::1. Is there a way to simulate external IP addresses while testing locally?
You can create a service for retrieving remote address. Define an interface for it and create 2 implementations and inject them depending on the current environment
public interface IRemoteIpService
{
IPAddress GetRemoteIpAddress();
}
public class RemoteIpService : IRemoteIpService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public RemoteIpService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public IPAddress GetRemoteIpAddress()
{
return _httpContextAccessor.HttpContext.Connection.RemoteIpAddress;
}
}
public class DummyRemoteIpService : IRemoteIpService
{
public IPAddress GetRemoteIpAddress()
{
//add your implementation
return IPAddress.Parse("120.1.1.99");
}
}
Startup
if (HostingEnvironment.IsProduction())
{
services.AddScoped<IRemoteIpService, RemoteIpService>();
}
else
{
services.AddScoped<IRemoteIpService, DummyRemoteIpService>();
}
Usage
public class TestController : Controller
{
//...
private readonly IRemoteIpService _remoteIpService;
public TestController(IRemoteIpService remoteIpService)
{
//...
_remoteIpService = remoteIpService;
}
//..
[HttpGet]
public IActionResult Test()
{
var ip = _remoteIpService.GetRemoteIpAddress();
return Json(ip.ToString());
}
}
For getting external ip for localhost, you need to send request to retrive the ip, and you could implement an extension for ConnectionInfo like
public static class ConnectionExtension
{
public static IPAddress RemotePublicIpAddress(this ConnectionInfo connection)
{
if (!IPAddress.IsLoopback(connection.RemoteIpAddress))
{
return connection.RemoteIpAddress;
}
else
{
string externalip = new WebClient().DownloadString("http://icanhazip.com").Replace("\n","");
return IPAddress.Parse(externalip);
}
}
}
And use like
var ip = Request.HttpContext.Connection.RemotePublicIpAddress();
Related
I got stuck and need some advice or pointer to a solution.
A web API with ASP.NET Core 3.1
Startup.cs
services.AddSingleton<ITopicClient>(s => new TopicClient({connectionstring},{topic}));
TopicRepository.cs
public class TopicRepository : ITopicRepository
{
private readonly ITopicClient _topicClient1;
private readonly ITopicClient _topicClient2;
public TopicRepository (ITopicClient topicClient1, ITopicClient topicClient2)
{
_topicClient1 = topicClient1;
_topicClient2 = topicClient2;
}
public async Task<Response> SendToTopicAsync(string message, string topic)
{
if( topic == "topic1")
await _topicClient1.send(message);
else if (topic == "topic2")
await _topicClient2.send(message);
}
}
TopicClient.cs in a shared library
public TopicClient(string serviceBusConnectionString, string topicName)
{
_topicClient = new TopicClient(_serviceBusConnectionString,topicName);
}
I need to send message to different topics. I would like to register services with different topic names in startup.cs. I want to reuse topicClient connection.
services.AddSingleton(s => new TopicClient({connectionstring},{topic1}));
services.AddSingleton(s => new TopicClient({connectionstring},{topic2}));
How can I achieve this by registering singleton instances of same type using same interface ?
Thank you in advance!
You could use a client resolver that holds your registered clients with a wrapper around the client.
First create a wrapper around your client with a name or enum for how to resolve it. As I'm not a fan of magic strings I decided to go with an enum in the example.
// Wrapper for your TopicClients
public interface ICustomTopicClient
{
public ITopicClient TopicClient { get; }
public TopicName TopicName { get; }
}
// Implement the ICustomTopicClient interface
public class CustomTopicClient : ICustomTopicClient
{
public ITopicClient TopicClient { get; }
public TopicName TopicName { get; }
public CustomTopicClient(ITopicClient topicClient, TopicName topicName)
{
TopicClient = topicClient;
TopicName = topicName;
}
}
// Enum for how to resolve the requested TopicClient
public enum TopicName
{
Topic1 = 0,
Topic2 = 1
}
// Register all ICustomTopicClients in your container
services.AddSingleton<ICustomTopicClient>(s => new CustomTopicClient(new TopicClient({connectionstring},{topic}), TopicName.Topic1));
services.AddSingleton<ICustomTopicClient>(s => new CustomTopicClient(new TopicClient({connectionstring},{topic2}), TopicName.Topic2));
Then you create a resolver that holds all custom clients.
You inject the collection of clients from the container and create a dictionary with a public method to resolve the clients.
public interface IMessageClientResolver
{
ITopicClient ResolveClient(TopicName name);
}
public class MessageClientResolver : IMessageClientResolver
{
private readonly Dictionary<TopicName, ITopicClient> topicClients;
public MessageClientResolver(IEnumerable<ICustomTopicClient> clients)
{
topicClients = clients.ToDictionary(k => k.TopicName, v => v.TopicClient);
}
public ITopicClient ResolveClient(TopicName name)
{
topicClients.TryGetValue(name, out var client);
if (client is null)
throw new ArgumentException(nameof(client));
return client;
}
}
Register the resolver to the container.
services.AddSingleton<IMessageClientResolver, MessageClientResolver>();
And then use it like this:
public class Foo
{
private readonly ITopicClient topicClient;
private readonly ITopicClient topicClient2;
public Foo(IMessageClientResolver clientResolver)
{
topicClient = clientResolver.ResolveClient(TopicName.Topic1);
topicClient2 = clientResolver.ResolveClient(TopicName.Topic2);
}
}
You can use the same pattern and extend the resolver with IQueueClients. And add a resolve method to return the IQueueClient by a QueueName enum.
You can already register multiple instances as the same interface, so when you do:
services.AddSingleton<ITopicClient>(_ => new TopicClient("topic1"));
services.AddSingleton<ITopicClient>(_ => new TopicClient("topic2"));
you already added two instances to the container.
It is just when you resolve interface ITopicClient, you always get the last added instance. For example, if you resolve:
// instance = topic2
var instance = container.GetService<ITopicClient>();
If you need all instances, you should resolve / inject IEnumerable<ITopicClient>.
class TopicRepository
{
public TopicRepository(IEnumerable<ITopicClient> clients)
{
// clients contains topic1 and topic2
}
}
I am trying to get client IP and compare values with the configuration. If it matches need to return true/false. How do I make this variable accessible to web application? I am new to .NET core. thanks
I have followed this article to create middleware class but not sure how to pass variable from this context.
https://learn.microsoft.com/en-us/aspnet/core/security/ip-safelist?view=aspnetcore-2.2
public class SafeListMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<SafeListMiddleware> _logger;
private readonly string _adminSafeList;
public SafeListMiddleware(
RequestDelegate next,
ILogger<SafeListMiddleware> logger,
string adminSafeList)
{
_adminSafeList = adminSafeList;
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Method != "GET")
{
var remoteIp = context.Connection.RemoteIpAddress;
string[] ip = _adminSafeList.Split(';');
var bytes = remoteIp.GetAddressBytes();
var match = false;
foreach (var address in ip)
{
var testIp = IPAddress.Parse(address);
var rangeA = IPAddressRange.Parse(address);
if(rangeA.Contains(remoteIp))
{
match = true;
break;
}
}
}
await _next.Invoke(context);
}
}
}
I would create an interface such as:
public interface IIPChecker
{
bool IsSafe(IPAddress remoteIpAddress);
}
with an implementation:
public class IPChecker : IIPChecker
{
private readonly IPAddress[] _safeList;
public IPChecker(string safeList)
{
var _safeList = safeList
.Split(';')
.Select(IPAddress.Parse)
.ToArray();
}
public bool IsSafe(IPAddress remoteIpAddress)
{
return _safeList.Contains(remoteIpAddress);
}
}
and inject it in the controllers that need it:
public class ValuesController : ControllerBase
{
private readonly IIPChecker _ipChecker;
public ValuesController(IIPChecker ipChecker)
{
_ipChecker = ipChecker;
}
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
var isValid = _ipChecker.IsSafe(HttpContext.Connection.RemoteIpAddress);
.....
}
}
If you need this information in all controllers, you can change them to inherit from something like
public class IpCheckController : ControllerBase
{
private readonly IIPChecker _ipChecker;
public IpCheckController(IIPChecker ipChecker)
{
_ipChecker = ipChecker;
}
private bool IsSafe => _ipChecker.IsSafe(HttpContext.Connection.RemoteIpAddress);
}
To get the client RemoteIp and compare with the configuration values. you have to first define Http Accessor in the Startup file like below.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
and then in the Middleware access the RemoteIp using below code and compare the value.
var remoteIp = context.Request.HttpContext.Connection.RemoteIpAddress.ToString();
I have created a .net core API, which pushes a message in RabbitMQ queue. I have used IOptions to read configuration data from .json file and added it as dependency.
Below is the code of my controller:
[Route("api/[controller]")]
public class RestController : Controller
{
private RabbitMQConnectionDetail _connectionDetail;
public RestController(IOptions<RabbitMQConnectionDetail> connectionDetail)
{
_connectionDetail = connectionDetail.Value;
}
[HttpPost]
public IActionResult Push([FromBody] OrderItem orderItem)
{
try
{
using (var rabbitMQConnection = new RabbitMQConnection(_connectionDetail.HostName,
_connectionDetail.UserName, _connectionDetail.Password))
{
using (var connection = rabbitMQConnection.CreateConnection())
{
var model = connection.CreateModel();
var helper = new RabbitMQHelper(model, "Topic_Exchange");
helper.PushMessageIntoQueue(orderItem.Serialize(), "Order_Queue");
}
}
}
catch (Exception)
{
return StatusCode((int)HttpStatusCode.BadRequest);
}
return Ok();
}
}
Connection details class has the below properties
public class RabbitMQConnectionDetail
{
public string HostName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
Now I want to unit test it, but since I am going to test it against a blackbox, I'm not able to think of how to unit test it and looking for kind help.
ConnectionClass
public class RabbitMQConnection : IDisposable
{
private static IConnection _connection;
private readonly string _hostName;
private readonly string _userName;
private readonly string _password;
public RabbitMQConnection(string hostName, string userName, string password)
{
_hostName = hostName;
_userName = userName;
_password = password;
}
public IConnection CreateConnection()
{
var _factory = new ConnectionFactory
{
HostName = _hostName,
UserName = _userName,
Password = _password
};
_connection = _factory.CreateConnection();
var model = _connection.CreateModel();
return _connection;
}
public void Close()
{
_connection.Close();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_connection.Close();
}
}
~ RabbitMQConnection()
{
Dispose(false);
}
}
Helper class
public class RabbitMQHelper
{
private static IModel _model;
private static string _exchangeName;
const string RoutingKey = "dummy-key.";
public RabbitMQHelper(IModel model, string exchangeName)
{
_model = model;
_exchangeName = exchangeName;
}
public void SetupQueue(string queueName)
{
_model.ExchangeDeclare(_exchangeName, ExchangeType.Topic);
_model.QueueDeclare(queueName, true, false, false, null);
_model.QueueBind(queueName, _exchangeName, RoutingKey);
}
public void PushMessageIntoQueue(byte[] message, string queue)
{
SetupQueue(queue);
_model.BasicPublish(_exchangeName, RoutingKey, null, message);
}
public byte[] ReadMessageFromQueue(string queueName)
{
SetupQueue(queueName);
byte[] message;
var data = _model.BasicGet(queueName, false);
message = data.Body;
_model.BasicAck(data.DeliveryTag, false);
return message;
}
}
Tightly coupling your Controller to implementation concerns are making it difficult to test your controller without side-effects. From the sample you provided you have shown that you are encapsulating the 3rd part API implementations and only exposing abstractions. Good. You however have not created an abstraction that would allow you to mock them when testing. I suggest a refactor of the RabbitMQConnection to allow for this.
First have your own backing abstraction.
public interface IRabbitMQConnectionFactory {
IConnection CreateConnection();
}
And refactor RabbitMQConnection as follows
public class RabbitMQConnection : IRabbitMQConnectionFactory {
private readonly RabbitMQConnectionDetail connectionDetails;
public RabbitMQConnection(IOptions<RabbitMQConnectionDetail> connectionDetails) {
this.connectionDetails = connectionDetails.Value;
}
public IConnection CreateConnection() {
var factory = new ConnectionFactory {
HostName = connectionDetails.HostName,
UserName = connectionDetails.UserName,
Password = connectionDetails.Password
};
var connection = factory.CreateConnection();
return connection;
}
}
Take some time and review exactly what was done with this refactor. The IOptions was moved from the Controller to the factory and the RabbitMQConnection has also been simplified to do it's intended purpose. Creating a connection.
The Controller now would need to be refactored as well
[Route("api/[controller]")]
public class RestController : Controller {
private readonly IRabbitMQConnectionFactory factory;
public RestController(IRabbitMQConnectionFactory factory) {
this.factory = factory;
}
[HttpPost]
public IActionResult Push([FromBody] OrderItem orderItem) {
try {
using (var connection = factory.CreateConnection()) {
var model = connection.CreateModel();
var helper = new RabbitMQHelper(model, "Topic_Exchange");
helper.PushMessageIntoQueue(orderItem.Serialize(), "Order_Queue");
return Ok();
}
} catch (Exception) {
//TODO: Log error message
return StatusCode((int)HttpStatusCode.BadRequest);
}
}
}
Again note the simplification of the controller. This now allows the factory to be mocked and injected when testing and by extension allows the mocks to be used by the RabbitMQHelper. You can use your mocking framework of choice for dependencies or pure DI.
I dont think it is a unit test scenario. If you want to to test with external component ie database or message queue then i suggest you do it as integration test.
What we do is to have a sand box environment with component SQL database and azure message bus. We have code to correctly set the state for this component ie seed the database and clear the message bus. Then we run test on the environment and check the state of the database or message bus count etc.
Currently I have a challenge to unit test a production code. We have a function to retrieve an IP address from an incoming WCF messages.
public void DoSomething(){
var ipAddressFromMessage = GetIpFromWcfMessage();
var IpAddress = IPAddress.Parse(ipAddressFromMessage);
if(IpAddress.IsLoopback)
{
// do something
}
else
{
// do something else
}
}
private string GetIpFromWcfMessage()
{
OperationContext context = OperationContext.Current;
string ip = ...//use the IP from context.IncomingMessageProperties to extract the ip
return ip;
}
The question is, what should I do so that I could test the checking of the IP in the DoSomething()?
[Test]
Public void DoSomethingTest()
{
//Arrange...
// Mock OperationContext so that we can manipulate the ip address in the message
// Assert.
...
}
Should I change the way I use the Operation context in a way so that I can mock it(e.g. implement an interface and mock the implementation of the interface)?
I would wrap the call with a static helper:
public static class MessagePropertiesHelper
{
private static Func<MessageProperties> _current = () => OperationContext.Current.IncomingMessageProperties;
public static MessageProperties Current
{
get { return _current(); }
}
public static void SwitchCurrent(Func<MessageProperties> messageProperties)
{
_current = messageProperties;
}
}
Then in GetIpFromWcfMessage I would call:
private string GetIpFromWcfMessage()
{
var props = MessagePropertiesHelper.Current;
string ip = ...//use the IP from MessageProperties to extract the ip
return ip;
}
And I would be able to switch the implementation in the test scenario:
[Test]
Public void DoSomethingTest()
{
//Arrange...
// Mock MessageProperties so that we can manipulate the ip address in the message
MessagePropertiesHelper.SwitchCurrent(() => new MessageProperties());
// Assert.
...
}
Here you can find my answer to similar problem: https://stackoverflow.com/a/27159831/2131067.
I have application in which I am showing data from sensors using SignalR. It uses ASP.net membership to authenticate the users. It all works fine if I only open one browser window(e.g. Firefox). If I open same website in another browser e.g. Chrome at the same time then signalR connection to firefox browser drops even if the user is different. This is what I am using to broadcast message:
Hub
[Authorize]
public class DataHub:Hub
{
private readonly RealTimeData _sensor;
public DataHub() : this(RealTimeData.Instance) { }
public DataHub(RealTimeData data)
{
_sensor = data;
}
public override Task OnConnected()
{
// _sensor.UserId = Context.ConnectionId; changed to
_sensor.UserId = Membership.GetUser().ProviderUserKey.ToString();
return base.OnConnected();
}
}
public class RealTimeData
{
//User Id
public String UserId { get; set; }
private readonly static Lazy<RealTimeData> _instance = new Lazy<RealTimeData>(() => new RealTimeData(GlobalHost.ConnectionManager.GetHubContext<DataHub>().Clients));// Singleton instance
private IHubConnectionContext Clients;
private void BroadcastDataOfAllSensors(List<SensorDetails> sensor)
{
//Clients.Client(UserId).updateDashboard(sensor);changed to
Clients.User(UserId).updateDashboard(sensor);
}
}
Application Startup
public class StartUp
{
public void Configuration(IAppBuilder app)
{
var idProvider = new UserIdProvider();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
app.MapSignalR();
}
}
UserId
public class UserIdProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
var userId = Membership.GetUser().ProviderUserKey;
return userId.ToString();
}
}