MVC3 request delay needed - asp.net-mvc-3

I have a MVC3 application. I have to add a delay in one uri request. I know the simplest way to do is an sleep, but the application in production has a big number of request and i'm afraid that let the iis without threads to response to other request.
I'm looking at the async controller/action for the solution.
Is better for the iis thead pool to do the thread.sleep inside the "actionSync" method?
How can i solve this delay? Maybe an async action that calls a new Web Service that does the thread.sleep inside?
AsyncManager.OutstandingOperations.Increment();
DelayService delayService = new DelayService();
delayService.GetDelayCompleted += (sender, e) =>
{
AsyncManager.Parameters["result"] = e.Value;
AsyncManager.OutstandingOperations.Decrement();
};
newsService.GetDelayAsync(seconds);
In this case if i will deploy this web service to the same IIS that the mvc3 app affects the iis available threads for the mvc3 app? what if is deployed in a different application pool?
What can ido?

Can you consider using the new ASP.NET MVC 4 bits which were released by Microsoft?
If so, this might be a possible solution:
public class TestController : AsyncController {
public async Task<ActionResult> GetData() {
await Task.Delay(5000);
return Content("Done");
}
}

Related

WebAPI return 404 on PUT/DELETE operations

This seems to be a fairly common issue, but none of the SO articles I have looked at have solved this for me.
I am working on a ASP.NET WebForms/MVC application running on IIS on Windows 10 (so not IIS Express) which is using jQuery AJAX to invoke a WebAPI application on a separate server. To get around CORS issues, and to add additional processing to all API calls, we implemented a server-side proxy using MVC controllers, so each call would end up somewhere like this:
[HttpPost]
public ActionResult Timesheets_Submit(Timesheet data)
{
var processedData = ProcessTheRequestInSomeWay(data);
var client = new SdkClient();
var results = client.Timesheets.Post(processedData);
return Json(results);
}
And this all worked quite successfully.
However, we are getting rather fed up of having to implement new server-side proxy methods each time we add a new API endpoint, so we decided to create a transparent server-side proxy using WebAPI, and have that do the real work.
The transparent server-side proxy is implemented like this:
public class TransparentProxyDelegatingHandler : DelegatingHandler
{
private static readonly Uri BaseUri = new Uri("https://my.apiserver.com");
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-Forwarded-For", request.GetClientIpAddress());
request.RequestUri = new Uri(BaseUri, request.RequestUri.PathAndQuery.Replace("/Proxy", string.Empty));
ProcessRequestInSomeWay(request);
var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
return response;
}
}
So a request to POST /Proxy/Timesheets will get translated into a call to POST https://my.apiserver.com/Timesheets and the response returned pretty much as-is.
The problem that I am having is that calls which use the PUT and DELETE verbs are being rejected as 404 Not Found by my UI (not by the API, I can still invoke that directly using e.g. Fiddler/Postman); the original proxy used those verbs, so it's not like they haven't been configured, it's just when I'm calling the delegating handler. The handler never gets invoked, so there's something happening in the routing engine that is causing MVC PUT/DELETE requests to work, but WebAPI PUT/DELETE requests to fail.
It turns out I was not registering the TransparentProxyDelegatingHandler correctly; I was registering it like this in my WebApiConfig:
configuration.MessageHandlers.Add(new TransparentProxyDelegatingHandler());
but as it turns out (thanks to https://blog.kloud.com.au/2013/11/24/do-it-yourself-web-api-proxy/), what I really wanted was:
configuration.Routes.MapHttpRoute(name: "proxy", routeTemplate: "proxy/{*path}",
handler: HttpClientFactory.CreatePipeline(
innerHandler: new HttpClientHandler(),
handlers: new DelegatingHandler[]
{
new TransparentProxyDelegatingHandler(),
}),
defaults: new { path = RouteParameter.Optional },
constraints: null);
I'm guessing that what was going on was that because I didn't have any actual ApiController implementations wired up to WebApi, it wasn't resolving correctly somehow in the early stages of the pipeline.

How to set up Web API Routing for a Proxy Controller?

Part of my application needs to act as a Proxy Server for a third party RESTful web service. Is there a way to set up Web API routing so that all requests of the same type will go to the same method?
For example, if the client sends in either of these GET requests I want them to go into a single GET action method that then sends on the request to the downstream server.
api/Proxy/Customers/10045
api/Proxy/Customers/10045/orders
api/Proxy/Customers?lastname=smith
The single action method for GET would pick up any one of these three requests and send them on to the respective service (I know how to work with HttpClient to make that happen effectively):
http://otherwebservice.com/Customers/10045
http://otherwebservice.com/Customers/10045/orders
http://otherwebservice.com/Customers?lastname=smith
I don't want to have to tightly couple my web service to the third party web service and replicate their entire API as method calls inside mine.
One workaround that I have thought of is to simply encode the target URL in JavaScript on the client and pass this into the Web API which will then only see one parameter. It would work, but I'd prefer to use the routing capabilities in Web API if possible.
Here's how I got this to work. First, create a controller with a method for each verb you want to support:
public class ProxyController : ApiController
{
private Uri _baseUri = new Uri("http://otherwebservice.com");
public async Task<HttpResponseMessage> Get(string url)
{
}
public async Task<HttpResponseMessage> Post(string url)
{
}
public async Task<HttpResponseMessage> Put(string url)
{
}
public async Task<HttpResponseMessage> Delete(string url)
{
}
}
The methods are async because they're going to use an HttpClient. Map your route like this:
config.Routes.MapHttpRoute(
name: "Proxy",
routeTemplate: "api/Proxy/{*url}",
defaults: new { controller = "Proxy" });
Now back to the Get method in the controller. Create an HttpClient object, create a new HttpRequestMessage object with the appropriate Url, copy everything (or almost everything) from the original request message, then call SendAsync():
public async Task<HttpResponseMessage> Get(string url)
{
using (var httpClient = new HttpClient())
{
string absoluteUrl = _baseUri.ToString() + "/" + url + Request.RequestUri.Query;
var proxyRequest = new HttpRequestMessage(Request.Method, absoluteUrl);
foreach (var header in Request.Headers)
{
proxyRequest.Headers.Add(header.Key, header.Value);
}
return await httpClient.SendAsync(proxyRequest, HttpCompletionOption.ResponseContentRead);
}
}
The URL combining could be more sophisticated, but that's the basic idea.
For the Post and Put methods, you'll also need to copy the request body
Also please note a HttpCompletionOption.ResponseContentRead parameter passed in SendAsync call, because without it, ASP.NET will spend an exremeley long time reading the content if the content is large (in my case, it changed a 500KB 100ms request into a 60s request).

Can't get ASP.NET Web API 2 Help pages working when using Owin

I've installed the correct package for Web Api 2
Install-Package Microsoft.AspNet.WebApi.HelpPage -Pre
But the help area is not being mapped and is returning 404 (Web Api working fine). I'm using Microsoft.Owin.Host.SystemWeb as the host. Below is my Startup code.
public class Startup
{
public void Configuration(IAppBuilder app)
{
//Required for MVC areas new HttpConfiguration() doesn't work with MVC
var config = GlobalConfiguration.Configuration;
AreaRegistration.RegisterAllAreas();
WepApiStartup.Configure(config);
app.UseWebApi(config);
}
}
GlobalConfiguration.Configuration is web host specific HttpConfiguraiton, which should only be used with web host scenario. Use it with OWIN host will cause unexpected issues.
Please use the following code instead:
public class Startup
{
public static HttpConfiguration HttpConfiguration { get; private set; }
public void Configuration(IAppBuilder app)
{
HttpConfiguration = new HttpConfiguration();
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(HttpConfiguration);
app.UseWebApi(HttpConfiguration);
}
}
Replace all GlobalConfiguration.Configuration with Startup.HttpConfiguration in the project include help page files.
Found the solution after a lot of digging/trial and error. The issue is well described here: http://aspnetwebstack.codeplex.com/discussions/453068
UseWebApi and UseHttpMessageHandler don't call Next OWIN's middleware other than for 404. This means if you use UseWebApi that's it, Next is never called therefore you can't use it with any other middleware (Nancy or Web Api Help pages for example).
Thanks to #aliostad patch:
https://github.com/aliostad/CacheCow/blob/master/samples/UsingCacheCowWithNancyAndOwin/HttpMessageHandlerAdapterModified.cs#L43
You can get it working as expected. I hope the team merge the pull request for this as UseWebApi breaks the Owin design goals IMO.
Update 13 Feb 2014
I've written an Owin extension to workaround this:
internal static void UseWebApiAndHelp(this IAppBuilder app, HttpConfiguration config)
{
WepApiStartup.Configure(config);
app.UseHandlerAsync((request, response, next) =>
{
if (request.Path == "/") //app.Map using a regex exclude list would be better here so it doesn't fire for every request
{
response.StatusCode = 301;
response.SetHeader("Location", "/Help");
return Task.FromResult(0);
}
return next();
});
// Map the help path and force next to be invoked
app.Map("/help", appbuilder => appbuilder.UseHandlerAsync((request, response, next) => next()));
app.UseWebApi(config);
}
Update 01 July 2015
You can also host the help pages using WebApi instead of MVC, which is great for self host http://blogs.msdn.com/b/yaohuang1/archive/2012/12/20/making-asp-net-web-api-help-page-work-on-self-hosted-services.aspx
Update 10 September 2015
For Web Api I tried #hongye-sun answer and it works too, follow what #gspatel says by changing HelpPageAreaRegistration.RegisterArea and the HelpController's constructor. My workaround works as well so pick whatever one works best for your situation.
However I'm still getting the issue when using UseWebApi with other middleware and it not invoking Next() (seems to only happen when using IIS not self host). I've found my workaround of mapping the path and forcing next to be invoked is a valid workaround for all Owin middleware Nancy, Simple.Web etc.
Update 13 Jan 2016
I've developed Owin middleware to generate the ASP.NET Web API Help pages we know and love that completely solves this problem. My blog post explains the background to this issue in detail

Accessing particular service method from metro app?

i have a web services which i am accessing in my client application(metro app) , but i want to access a particular method inside those many methods i have how should i do it ,
as of now , i am doing it in this way to accessing the web services from my metro app:-
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
string responseBodyAsText;
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://182.134.34.99/OE/examplewebservices.svc");
response.EnsureSuccessStatusCode();
StatusText.Text = response.StatusCode.ToString();
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
my requirement is :- there are many methods inside that examplewebservices , so i want to access one of the method inside that , pass input parameters to that method and get the result.
1)How to access one a particular method inside those many methods ( from metro app) ?
2)how to pass input to that service method (from metro app)?
Question might be very basic to you , pls help out. i am new to metro application development.
Thanks in advance.
The code you have does not call a service, it downloads service definition page. You will need to add a service reference to your project (right click on project node, choose Add Service Reference from context menu). Then you will be able to call methods of your service. In WinRT app, you will only be able to call web service asynchronously, so all methods will have 'Async' suffix and you will have to use async/await pattern when calling it.
To call an operation on the service you can use this pattern:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://182.134.34.99/OE/examplewebservices.svc");
HttpResponseMessage response = await client.GetAsync("MyOperation");
...
}
To send values in this simplistic example you can send them as QueryStrings appended to the MyOperation string as follows: MyOperation?myvalue=1 etc.
Other than that #Seva Titov gave a good response to the dynamic aspect.

SmtpClient.SendAsync blocking my ASP.NET MVC Request

I have a Action that sends a simple email:
[HttpPost, ActionName("Index")]
public ActionResult IndexPost(ContactForm contactForm)
{
if (ModelState.IsValid)
{
new EmailService().SendAsync(contactForm.Email, contactForm.Name, contactForm.Subject, contactForm.Body, true);
return RedirectToAction(MVC.Contact.Success());
}
return View(contactForm);
}
And a email service:
public void SendAsync(string fromEmail, string fromName, string subject, string body, bool isBodyHtml)
{
MailMessage mailMessage....
....
SmtpClient client = new SmtpClient(settingRepository.SmtpAddress, settingRepository.SmtpPort);
client.EnableSsl = settingRepository.SmtpSsl;
client.Credentials = new NetworkCredential(settingRepository.SmtpUserName, settingRepository.SmtpPassword);
client.SendCompleted += client_SendCompleted;
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage));
}
private void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
Tuple<SmtpClient, MailMessage> data = (Tuple<SmtpClient, MailMessage>)e.UserState;
data.Item1.Dispose();
data.Item2.Dispose();
if (e.Error != null)
{
}
}
When I send a email, I am using Async method, then my method SendAsync return immediately, then RedirectToAction is called. But the response(in this case a redirect) isnĀ“t sent by ASP.NET until client_SendCompleted is completed.
Here's what I'm trying to understand:
When watching the execution in Visual Studio debugger, the SendAsync returns immediately (and RedirectToAction is called), but nothing happens in the browser until email is sent?
If i put a breakpoint inside client_SendCompleted, the client stay at loading.... until I hit F5 at debugger.
This is by design. ASP.NET will automatically wait for any outstanding async work to finish before finishing the request if the async work was kicked off in a way that calls into the underlying SynchronizationContext. This is to ensure that if your async operation tries to interact with the HttpContext, HttpResponse, etc. it will still be around.
If you want to do true fire & forget, you need to wrap your call in ThreadPool.QueueUserWorkItem. This will force it to run on a new thread pool thread without going through the SynchronizationContext, so the request will then happily return.
Note however, that if for any reason the app domain were to go down while your send was still in progress (e.g. if you changed the web.config file, dropped a new file into bin, the app pool recycled, etc.) your async send would be abruptly interrupted. If you care about that, take a look at Phil Haacks WebBackgrounder for ASP.NET, which let's you queue and run background work (like sending an email) in such a way that will ensure it gracefully finishes in the case the app domain shuts down.
This is an interesting one. I've reproduced the unexpected behaviour, but I can't explain it. I'll keep digging.
Anyway the solution seems to be to queue a background thread, which kind of defeats the purpose in using SendAsync. You end up with this:
MailMessage mailMessage = new MailMessage(...);
SmtpClient client = new SmtpClient(...);
client.SendCompleted += (s, e) =>
{
client.Dispose();
mailMessage.Dispose();
};
ThreadPool.QueueUserWorkItem(o =>
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage)));
Which may as well become:
ThreadPool.QueueUserWorkItem(o => {
using (SmtpClient client = new SmtpClient(...))
{
using (MailMessage mailMessage = new MailMessage(...))
{
client.Send(mailMessage, Tuple.Create(client, mailMessage));
}
}
});
With .Net 4.5.2, you can do this with ActionMailer.Net:
var mailer = new MailController();
var msg = mailer.SomeMailAction(recipient);
var tcs = new TaskCompletionSource<MailMessage>();
mailer.OnMailSentCallback = tcs.SetResult;
HostingEnvironment.QueueBackgroundWorkItem(async ct =>
{
msg.DeliverAsync();
await tcs.Task;
Trace.TraceInformation("Mail sent to " + recipient);
});
Please read this first: http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
I sent the bug to Microsoft Connect https://connect.microsoft.com/VisualStudio/feedback/details/688210/smtpclient-sendasync-blocking-my-asp-net-mvc-request

Resources