Im having trouble with this, I dont why, it must be something im doing wrong. I've had to rewrite to get it working but it smells all wrong, however it works.
So this is what I tried first and it does not work, as the statuscode of 500 was returned, but this is because it was not waiting for a response, I need it to wait
[HttpPost]
public async Task<JsonResult> Booking(string model)
{
//do some bits.
var a = new JavaScriptSerializer().Serialize(e);
var booking = new HttpClient();
HttpContent content = new StringContent(a,Encoding.UTF8);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await booking.PostAsync("https://webapi.domain.com/Booking/Post", content);
var aa = response.StatusCode //500 Internal Error
}
So I rewrote
[HttpPost]
public async Task<JsonResult> Booking(string model)
{
//do some bits.
var a = new JavaScriptSerializer().Serialize(e);
var booking = new HttpClient();
HttpContent content = new StringContent(a,Encoding.UTF8);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await booking.PostAsync("https://webapi.domain.com/Booking/Post", content);
var t = new Stopwatch();
while (response.StatusCode ==HttpStatusCode.InternalServerError)
{
t.Start();
var zzzz = response.ReasonPhrase;
if (t.ElapsedMilliseconds >10000)
{
response.StatusCode = HttpStatusCode.RequestTimeout;
t.Stop();
}
}
var aa = response.StatusCode //201 Created
}
And this works and returns me my 201, ugly, but can anyone tell me and show me what i'm doing wrong?
The server has a timing dependent bug. It goes away when you use the debugger to give it enough time to avoid the crash.
An asynchronous module or handler completed while an asynchronous operation was still pending
Looks like something with async.
The client is not at fault and the while (response.StatusCode ==HttpStatusCode.InternalServerError) loop does nothing. It does not even change the timing at the server. Your usage of the debugger probably did that and the two effect were confounded in your interpretation.
Fix the server, now that you know where to look.
Related
When implementing calls to CreateAsync in the Hl7.Fhir.Rest.FhirClient library I'm struggling with how to mock a valid response. I know how to mock the dotnet-httpclient using a Mock HttpMessageHandler object and noticed there is a message handler argument that can be specified when creating the FhirClient. What I have tried to do is specify a message handler to the creation step that is a mock message handler object.
This simplified unit test attempts to mock the HttpMessageHandler and cause it to return a valid body and result code from the FhirClient's CreateAsync method call.
[Fact]
public async Task SubscribeAndReturnSubscriptionIdAsync()
{
var mockHttpMessageHandler = MockFhirHttpClientMessageHandler.MockSubscribeMessageResponse(new StringContent("{'id':'abc123','status':'active'}"), HttpStatusCode.Created);
var subscriptionResource = new Subscription()
{
Criteria = "https://server.fire.ly/CareTeam",
Status = Subscription.SubscriptionStatus.Active,
Reason = "test",
Channel = new Subscription.ChannelComponent()
{
Type = Subscription.SubscriptionChannelType.RestHook,
Endpoint = "http://localhost:9999/AscomFhirApi/UpdateCareTeam",
Payload = "application/fhir+json"
},
};
var serverUri = new Uri("http://server.fire.ly");
var clientSettings = new FhirClientSettings()
{
PreferredFormat = ResourceFormat.Json
};
var fhirHttpClient = new Hl7.Fhir.Rest.FhirClient(serverUri, clientSettings, mockHttpMessageHandler.Object);
var subscription = await fhirHttpClient.CreateAsync<Subscription>(subscriptionResource);
Assert.NotEmpty(subscription.Id);
}
The MockSubscribeMessageResponse method shown below creates the HttpMessageHandler that is passed to the FhirClient in the above test.
public static Mock<HttpMessageHandler> MockSubscribeMessageResponse(
HttpContent content,
HttpStatusCode code = HttpStatusCode.OK)
{
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = code,
Content = content
});
return mockHttpMessageHandler;
}
The error I'm getting is a Null Reference Exception in what looks like the HttpResponseMessage or response body.
System.NullReferenceException
Object reference not set to an instance of an object.
at Hl7.Fhir.Rest.HttpToEntryExtensions.ToEntryResponse(HttpResponseMessage response, Byte[] body)
at Hl7.Fhir.Rest.HttpClientRequester.ExecuteAsync(EntryRequest interaction)
at Hl7.Fhir.Rest.BaseFhirClient.executeAsync[TResource](Bundle tx, IEnumerable`1 expect)
at Tests.Unit.Core.Services.FirelyHttpClientShould.SubscribeAndReturnSubscriptionIdAsync() in C:\src\AscomIASharedAssignFHIRApi5\Tests.Unit.Core\Services\FirelyHttpClientShould.cs:line 60
You have probably figured this out long time ago, but the source of error is most probably missing RequestMessage, implementation of ToEntryResponse depends on response.RequestMessage.RequestUri being set. So I guess that what you need to do is:
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = code,
RequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost"),
Content = content
});
return mockHttpMessageHandler;
A long time later again ...
In version 3.8.3 of the Firely SDK the FhirClient now has support for taking the HttpClient in its constructor, so this may make unit testing apis much easier. It wasn't clear from your post what you were testing here...
I wrote a blog post on using it for this type of testing
Roughly something like ...
[TestMethod]
public async Task SubscribeAndReturnSubscriptionIdAsync()
{
using (var fhirServerFactory = new UnitTestFhirServerApplication())
using (var httpclient = fhirServerFactory.CreateClient())
{
var server = new FhirClient("http://server.fire.ly", httpclient);
var subscriptionResource = new Subscription()
{
Criteria = "https://server.fire.ly/CareTeam",
Status = Subscription.SubscriptionStatus.Active,
Reason = "test",
Channel = new Subscription.ChannelComponent()
{
Type = Subscription.SubscriptionChannelType.RestHook,
Endpoint = "http://localhost:9999/AscomFhirApi/UpdateCareTeam",
Payload = "application/fhir+json"
},
};
var subscription = await server.CreateAsync(subscriptionResource);
// ...
}
}
Im creating a list of components in Blazor, each one of these components need to request some data from a webpage. The list are created as follows on a .razor page:
#foreach(stringcomp in Complist){
<myComponent />
}
around 100 components are created. On all of these components the following URL request is preformed (using this code):
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await GetUrl("some url here");
}
}
public async Task<string> GetUrl(string url)
{
HttpClient client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("User-Agent", "get data service");
var response = await client.SendAsync(request).ConfigureAwait(false);
string res = null;
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var streamReader = new StreamReader(responseStream);
res = await streamReader.ReadToEndAsync().ConfigureAwait(false);
}
return res;
}
Doing this I'm running in to some problems where most of my calls to SendAsync never returns a value. I have come to understand that this is because of a lock-state but for the life of me can't figure out how to solve it. most similar answers suggest setting .ConfigureAwait(false) but this does not yeald a different result in my case.
So my question is: Hos can i request webbpages simultaneously in different components and be sure that they won't hang/lookup. As theres many requests that some times takes a long time (5-10 sec) to complete it's not an alternative to do them synchronously.
It might also be of importance to mention that me code and pages are separated, every .razor page are using #inherits to get its functions/logic
Try to use IHttpClientFactory as follows:
[Inject] public IHttpClientFactory clientFactory { get; set;}
using System.IO;
Your GetUrl method:
public async Task<string> GetUrl(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Accept", "application/json");
request.Headers.Add("User-Agent", "get data service");
var client = clientFactory.CreateClient();
var response = await client.SendAsync(request);
string res = null;
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
var streamReader = new StreamReader(responseStream);
res = await streamReader.ReadToEndAsync().ConfigureAwait(false);
}
return res;
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// More code here...
}
Hope this works...
I have a number of calls I make to a webapi client which return a Task
something like this
public async Task<TResp> GetMyThingAsync(TReq req)
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(BaseURI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));
await HttpRuntime.Cache.GetToken().ContinueWith((t) =>
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("XXX", t.Result);
});
var httpResponseMessage = await client.PostAsXmlAsync<TReq>("This/That/", req);
httpResponseMessage.EnsureSuccessStatusCode();
var resp = await httpResponseMessage.Content.ReadAsAsync<TResp>();
return resp;
}
}
the calls to the api can of course return 500's or some other problem. EnsureSuccessStatusCode() obviously throws if something like that happens, but then its too late to do anything with any information in the response.
is there a nice way of dealing with this?
I understand you can add a messageHandler with the client, something like
HttpClient client = HttpClientFactory(new ErrorMessageHandler()) ..
var customHandler = new ErrorMessageHandler()
{ InnerHandler = new HttpClientHandler()};
HttpClient client = new HttpClient(customHandler);
is this the way to go? what would the ErroMessageHandler look like and do to return something useful to the calling controller...?
thanks muchly
nat
Creating a custom handler can be an elegant solution to go about logging the exception or validating the response. I am not sure if the called controller is waiting for a meaningful response from the clients end if it encounters an exception. I think the real important part is to make sure you (the client) handle the web apis errors gracefully.
This can be done in a couple of ways:
You can handle exceptions locally inside the calling method. You can use the HttpResponseMessage.IsSuccessStatusCode property which indicated if a bad response returned instead of calling httpResponseMessage.EnsureSuccessStatusCode() which throws an exception, and return a custom ErrorResponse (or do whatever you decide):
var client = new HttpClient() // No need to dispose HttpClient
client.BaseAddress = new Uri(BaseURI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));
var token = await HttpRuntime.Cache.GetToken();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("XXX", token);
var httpResponseMessage = await client.PostAsXmlAsync<TReq>("This/That/", req);
if (!httpResponseMessage.IsSuccessStatusCode)
{
return Task.FromResult(new ErrorResponse()) // Create some kind of error response to indicate failure
}
var resp = await httpResponseMessage.Content.ReadAsAsync<TResp>();
return resp;
Create a ErrorLoggingHandler which can log exceptions (or do something else) received from the web api:
public class ErrorLoggingHandler : DelegatingHandler
{
private readonly StreamWriter _writer; // As a sample, log to a StreamWriter
public ErrorLoggingHandler(Stream stream)
{
_writer = new StreamWriter(stream);
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (!response.IsSuccessStatusCode)
{
// This would probably be replaced with real error
// handling logic (return some kind of special response etc..)
_writer.WriteLine("{0}\t{1}\t{2}", request.RequestUri,
(int) response.StatusCode, response.Headers.Date);
}
return response;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_writer.Dispose();
}
base.Dispose(disposing);
}
}
Then, you can create your HttpClient using HttpClientFactory:
var httpclient = HttpClientFactory.Create(new ErrorLoggingHandler(new FileStream(#"Location", FileMode.OpenOrCreate)));
In my Web API app, I'm using HttpServer to contain my controller in unit tests, and I'm using HttpClient to call it directly, eg:
[Fact]
public void TestMyController()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
var server = new HttpServer(config);
var client = new HttpClient(server);
var response = client.GetAsync("http://localhost/api/test/values").Result;
}
I've noticed (by stepping through the debugger, and confirmed on other SO posts), that the JsonFormatter is not really running - it's initialized, but not exercised. Since this test isn't opening a socket, and the HttpClient is directly invoking the HttpServer through the HttpMessageHandler API, it does make sense that formatting/serialization isn't run because it's not needed.
In my case, I have some custom formatting/serialization/deserialization code that isn't being hit during these tests, but it's hit when I run in a real web server. I'd like to exercise that code in these tests; and it also just seems risky to exclude the serialization/deserialization code path when testing. Any advice on this?
Following is a quick example of what you could do to force formatters to go through serialization/deserialization. Here we are converting ObjectContent to StreamContent. In the below code, the call to CopyToAsync triggers a path where formatters are forced to serialize. In case of deserilization, in order to make sure we go through formatters we want the content to be of type other than ObjectContent as ReadAsAsync has internal logic which special cases ObjectContnent and we want to circumvent it.
HttpClient client = new HttpClient(new InMemoryHttpContentSerializationHandler(new HttpServer(config)));
public class InMemoryHttpContentSerializationHandler : DelegatingHandler
{
public InMemoryHttpContentSerializationHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Content = await ConvertToStreamContentAsync(request.Content);
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Content = await ConvertToStreamContentAsync(response.Content);
return response;
}
private async Task<StreamContent> ConvertToStreamContentAsync(HttpContent originalContent)
{
if (originalContent == null)
{
return null;
}
StreamContent streamContent = originalContent as StreamContent;
if (streamContent != null)
{
return streamContent;
}
MemoryStream ms = new MemoryStream();
await originalContent.CopyToAsync(ms);
// Reset the stream position back to 0 as in the previous CopyToAsync() call,
// a formatter for example, could have made the position to be at the end
ms.Position = 0;
streamContent = new StreamContent(ms);
// copy headers from the original content
foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
{
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return streamContent;
}
}
Sorry if this is a easy question but I am totally new to WP7.
I have a rest service that I am trying to consume however I get an error 'Invalid cross-thread access.'
This is my code
public ObservableCollection<TransactionViewModel> Transactions { get;private set; }
public MainViewModel()
{
this.Transactions = new ObservableCollection<TransactionViewModel>();
}
public void LoadTransactions(string id)
{
var req = (HttpWebRequest)WebRequest.Create(string.Format("http://domain.com/Transactions?Id={0}", id));
req.Method = "POST";
req.ContentType = "application/json; charset=utf-8";
// call async
req.BeginGetResponse(new AsyncCallback(jsonGetRequestStreamCallback), req);
this.IsDataLoaded = true;
}
void jsonGetRequestStreamCallback(IAsyncResult asynchronousResult)
{
WebResponse response = ((HttpWebRequest)asynchronousResult.AsyncState).EndGetResponse(asynchronousResult);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string responseString = reader.ReadToEnd();
reader.Close();
var s = JsonConvert.DeserializeObject<List<TransactionViewModel>>(responseString);
foreach (var t in s)
{
Transactions.Add(new TransactionViewModel()
{
.........
}
}
Have I done something really stupid here?
When you come back from the request you are no longer on the UI thread. So you need to switch control back to the UI thread before performing any actions that will affect the UI.
You are updating an ObservableCollection, which will be bound on the UI and therefore the update is going to affect the UI.
There are a number of approaches, the simplest for you purposes will be
Deployment.Current.Dispatcher.BeginInvoke(()=> {
foreach (var t in s) {
Transactions.Add(new TransactionViewModel());
}
});
Edit: Also if you want to read a little more about this, I have a blog post about it here http://csainty.blogspot.com/2010/10/windows-phone-7asynchronous-programming.html it starts from code like yours that looks reasonable and should work, explains a few of the gotchas and how to get it working.