I am getting an exception that "The connection has not been established" when I try to invoke my hub class method using IHubProxy and I am unable figure out the issue. The connection has been made successful but the following line of code throw an error.
hubProxy.Invoke("SendNotificationToUser", new object[] { touser, message }).ContinueWith(task =>
{
if (task.IsFaulted && task.Exception != null)
{
// log error
}
});
here's my complete code..
[WebMethod]
public static void NotfTest(string message)
{
var hubConnection = new HubConnection("http://localhost:3052/CollegeBuilder/");
IHubProxy hubProxy = hubConnection.CreateHubProxy("NotificationHub");
var touser = "128";
try
{
lock (hubConnection)
{
if (hubConnection.State == Microsoft.AspNet.SignalR.Client.ConnectionState.Disconnected)
{
hubConnection.Start().Wait(2000);
}
}
hubProxy.Invoke("SendNotificationToUser", new object[] { touser, message }).ContinueWith(task =>
{
if (task.IsFaulted && task.Exception != null)
{
// log error
}
}); ;
}
finally
{
hubConnection.Stop();
}
}
In order to debug your situation you should enable detailed errors. See here for more information: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-javascript-client#handleerrors
Related
I am trying to catch and format the exception thrown by the resource filter but getting this error. The middleware is working for exceptions thrown from controller level but getting this - "System.InvalidOperationException: Headers are read-only, response has already started" error while trying to write to the response in case of resource level errors.
Code of my Resource Filter:
public class TestingAsyncResourceFilter : IAsyncResourceFilter
{
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
Console.WriteLine("Resource filter executing");
var resourceExecutedContext = await next();
Console.WriteLine("Resource filter executed");
if (!resourceExecutedContext.ModelState.IsValid)
{
throw new CustomUPException();
}
}
}
Code of middleware:
public class ResponseFormatterMiddleware : IMiddleware
{
private readonly ILogger<ResponseFormatterMiddleware> _logger;
public ResponseFormatterMiddleware(ILogger<ResponseFormatterMiddleware> logger)
{
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
try
{
Console.WriteLine("Before execution");
await next(context);
Console.WriteLine("After Execution");
}
catch(CustomUPException e)
{
Console.WriteLine("Here we are");
await context.Response.WriteAsJsonAsync(
new ResponseDto()
{
statusCode = e.statusCode,
message = e.message
}); // getting error
}
catch(Exception e)
{
_logger.LogError(e.Message);
context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
await context.Response.WriteAsJsonAsync(
new ResponseDto()
{
success = false,
message = "Request failed"
});
}
}
}
Code of my controller:
[Route("api/[controller]")]
[ApiController]
public class TestingController : ControllerBase
{
[HttpPost("/resource")]
public async Task<UserDto> testingResource( [FromBody] UserDto dto)
{
if (dto.email.Contains("hell"))
{
throw new CustomUPException(); //working
}
return dto;
}
}
Instead of using resource filter, I have used this strategy for formatting model validation errors as the documentation suggests.
Curious to know more about the raised issue though. Thanks in advance
// Add services to the container.
builder.Services.AddControllers().ConfigureApiBehaviorOptions(
options =>
{
options.InvalidModelStateResponseFactory = context =>
{
if (!context.ModelState.IsValid)
{
var data = new Dictionary<string, string?>();
//My Response formatter
var modelStateDictionary = context.ModelState;
foreach (var key in modelStateDictionary.Keys)
{
var errors = modelStateDictionary[key]?.Errors;
data.TryAdd(key, errors?[0].ErrorMessage);
}
return new ObjectResult(new UniversalResponseDto()
{
data = data,
statusCode = (int)HttpStatusCode.UnprocessableEntity,
sucess = false,
message = "One or more validation error occured"
})
{
StatusCode = (int)HttpStatusCode.UnprocessableEntity,
};
}
return new ObjectResult(context.HttpContext.Response);
};
});
I have a Xamarin iOS application I am building. For the Authentication and Authorization I am using Identity Server. To logon I am using IdentityModel.OidcClient using version 4.0.0.
When a user click on a Login button I call the Identity Server using:
_result = await _client.LoginAsync(new LoginRequest());
I am redirected to my identity server and can succesfully login. After login the browser disappears but the code never reaches the next line:
_result = await _client.LoginAsync(new LoginRequest());
if (_result.IsError)
{
return;
}
I have found many example and they all tell me to do the following. I have also tried with the demo Identity Server and same issue.
This is the IdentityModel.OidcClient.Browser.IBrowser implementation:
public class ASWebAuthenticationSessionBrowser : IBrowser
{
ASWebAuthenticationSession _asWebAuthenticationSession;
public ASWebAuthenticationSessionBrowser()
{
Debug.WriteLine("ctor");
}
public Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<BrowserResult>();
try
{
_asWebAuthenticationSession = new ASWebAuthenticationSession(
new NSUrl(options.StartUrl),
new NSUrl(options.EndUrl).Scheme,
(callbackUrl, error) =>
{
tcs.SetResult(CreateBrowserResult(callbackUrl, error));
_asWebAuthenticationSession.Dispose();
});
// iOS 13 requires the PresentationContextProvider set
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
_asWebAuthenticationSession.PresentationContextProvider = new PresentationContextProviderToSharedKeyWindow();
_asWebAuthenticationSession.Start();
}
catch (Exception ex)
{
throw;
}
return tcs.Task;
}
class PresentationContextProviderToSharedKeyWindow : NSObject, IASWebAuthenticationPresentationContextProviding
{
public UIWindow GetPresentationAnchor(ASWebAuthenticationSession session)
{
return UIApplication.SharedApplication.KeyWindow;
}
}
private static BrowserResult CreateBrowserResult(NSUrl callbackUrl, NSError error)
{
if (error == null)
return new BrowserResult
{
ResultType = BrowserResultType.Success,
Response = callbackUrl.AbsoluteString
};
if (error.Code == (long)ASWebAuthenticationSessionErrorCode.CanceledLogin)
return new BrowserResult
{
ResultType = BrowserResultType.UserCancel,
Error = error.ToString()
};
return new BrowserResult
{
ResultType = BrowserResultType.UnknownError,
Error = error.ToString()
};
}
}
I am using Xamarin.Forms and netstandard 2.0.
My redirect uri: RedirectUri = "wodtracker.app://callback"
I have created a library project for writing logs into ApplicationInsights as well as table storage and is being consumed my different other WebAPI projects. But due to some reason the logs are not getting logged in Application Insights but it works with table storage.
private void AddTelemetryTarget(string instrumentationKey, LoggerEnumerations.LogLevel minLogLevel, LoggingConfiguration config)
{
try
{ ConfigurationItemFactory.Default.Targets.RegisterDefinition("ApplicationInsightsTarget", typeof(ApplicationInsightsTarget));
ApplicationInsightsTarget aiTarget = new ApplicationInsightsTarget();
aiTarget.InstrumentationKey = instrumentationKey;
aiTarget.Name = "ai";
var wrapper = new AsyncTargetWrapper(aiTarget, 5000, AsyncTargetWrapperOverflowAction.Grow);
config.AddTarget("TelemetryAsyncWrapper", wrapper);
//Applying logging rules.
LoggingRule rule = new LoggingRule("*", ConvertLogType(minLogLevel), aiTarget);
config.LoggingRules.Add(rule);
}
catch
{ }
}
private LogLevel ConvertLogType(LoggerEnumerations.LogLevel type)
{
switch (type)
{
case LoggerEnumerations.LogLevel.Error: return LogLevel.Error;
case LoggerEnumerations.LogLevel.Info: return LogLevel.Info;
case LoggerEnumerations.LogLevel.Warn: return LogLevel.Warn;
default: return LogLevel.Trace;
}
}
public async Task Log(string message, LoggerEnumerations.LogLevel type, Dictionary<string, string> customParams, Exception ex = null, bool isPayload = false)
{
LogEventInfo eventInfo = PopulateEventInfo(message, type, customParams, ex);
if (!isPayload)
{
_logger.Log(eventInfo);
}
else
{
_payloadLogger.Log(eventInfo);
}
}
private LogEventInfo PopulateEventInfo(string message, LoggerEnumerations.LogLevel type, Dictionary<string, string> customParams, Exception ex = null)
{
LogEventInfo eventInfo = new LogEventInfo();
eventInfo.Level = ConvertLogType(type);
eventInfo.Message = message;
eventInfo.LoggerName = this.GetType().ToString();
if (ex != null)
{
eventInfo.Exception = ex;
}
else if (eventInfo.Level == LogLevel.Error)
{
eventInfo.Exception = new Exception(message);
}
//Adding custom properties to LogEventInfo to display in Application insight
if (customParams != null)
{
foreach (KeyValuePair<string, string> param in customParams)
{
eventInfo.Properties.Add(param.Key, param.Value);
}
}
return eventInfo;
}
Version of Nuget packages are
Microsoft.ApplicationInsights.NLogTarget : 2.13.1
NLog : 4.6.8
Thanks
I added Application Insights as Connected Services and I removed the Instrumentation Key from ApplicationInsights.config file and when registering the nlog target I used instrumentation key from my web.config file and it started working.
I'm using ApplicationInsights and I want to add the request, and after that the response, to the logging properties.
To achieve this I am implementing my own ITelemetryInitializer. It looks exactly like this.
public class MyInitializer : ITelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyInitializer(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null || _httpContextAccessor?.HttpContext?.Request == null
|| requestTelemetry.Properties.ContainsKey("RequestBody"))
{
return;
}
var request = _httpContextAccessor?.HttpContext?.Request;
request?.EnableRewind();
if (request.Method.Equals(HttpMethod.Post.ToString(), StringComparison.InvariantCultureIgnoreCase)
|| request.Method.Equals(HttpMethod.Put.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
var requestBody = reader.ReadToEnd();
requestTelemetry.Properties.Add("RequestBody", requestBody);
}
}
}
}
In startup I've added this
services.AddHttpContextAccessor();
services.AddSingleton<ITelemetryInitializer, MyInitializer>();
services.AddApplicationInsightsTelemetry();
The error I get is:
ObjectDisposedException: Cannot access a disposed object.
Object name: FileBufferingReadStream.
Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ThrowIfDisposed()
I've used .EnableRewind as well as instructing the StreamReader to leave the file open. Despite this my request is still null when it actually hits my controller, or even when it hits my initializer again for a second pass (setting the response information).
Any suggestions are welcome.
Additionally I tried adding a piece of middleware to ensure .EnableRewind was on for everything, but this did nothing. I'd prefer not to have to add any additional middleware since I'd like there to be no other dependencies.
app.Use(async (context, next) =>
{
context.Request.EnableRewind();
await next();
});
Thanks.
As always the solution ends up being a single line of code. I owe Mr Gunnar Peipman a thanks for his blog post Reading request body in ASP.NET Core.
The line:
request.Body.Seek(0, SeekOrigin.Begin);
The code
public class MyInitializer : ITelemetryInitializer
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MyInitializer(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry telemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
if (requestTelemetry == null || _httpContextAccessor?.HttpContext?.Request == null
|| requestTelemetry.Properties.ContainsKey("RequestBody"))
{
return;
}
var request = _httpContextAccessor?.HttpContext?.Request;
request?.EnableRewind();
if (request.Method.Equals(HttpMethod.Post.ToString(), StringComparison.InvariantCultureIgnoreCase)
|| request.Method.Equals(HttpMethod.Put.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
{
var requestBody = reader.ReadToEnd();
request.Body.Seek(0, SeekOrigin.Begin);
requestTelemetry.Properties.Add("RequestBody", requestBody);
}
}
}
}
I just start testing signalr and I am trying to add text to a rich text box after I got a response from my HUB class , but it doesn't work (no text is shown in my richtextbox) I don't know why...(the code run with no errors)
//server
public class ConnectByHub : Hub
{
public void testFunc(mas) {
string ans = mas + " got it";
Clients.All.testFunc(ans);
} }
//Client
private async void connectToServer()
{
Connection = new HubConnection(LocalClient);
HubProxy = Connection.CreateHubProxy("ConnectByHub");
try
{
await Connection.Start();
}
catch (Exception ex)
{
return;
}
string msg = "Hello friend!";
HubProxy.Invoke("testFunc", (msg)).Wait();
// Option one - doesn't work
HubProxy.On<string>("testFunc", (param) => Invoke((Action)(() => { MsgTxtBox.Text = "something happened"; })));
//Option two - doesn't work
HubProxy.On<string>("testFunc", (param) => this.Invoke((Action)(() => { MsgTxtBox.AppendText("Something happend " + Environment.NewLine); })));
}
I think part of the problem is trying to send a message from the same Async method (connectToServer) in which your listener is running.
I mostly used the same code from the question but moved a couple things around:
Moved HubProxy.Invoke() out of the Async method and called it from a button_click event
Called string.format() on the parameter
SERVER:
public class ConnectByHub : Hub
{
public void Send(string message)
{
Clients.All.testFunc(message);
}
}
CLIENT:
// Added button event
private void button1_Click(object sender, EventArgs e)
{
string msg = "Hello friend!";
HubProxy.Invoke("Send", msg).Wait();
}
private async void ConnectToServerAsync()
{
Connection = new HubConnection(LocalClient);
HubProxy = Connection.CreateHubProxy("ConnectByHub");
// Put the parmater in string.format()
HubProxy.On<string>("testFunc", (param) => this.Invoke((Action)(() => MsgTxtBox.AppendText(string.Format("{0}", param)))));
try
{
await Connection.Start();
}
catch (Exception ex)
{
richTextBox1.AppendText(string.Format("Unable to Connect to server ({0})", ServerURI));
return;
}
}