Web Api or Web Service [closed] - asp.net

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I read lots of about Web Api. For example i understand Web Service is a kind of Web Api or Web Api is more flexible.
But i didn't get that: Is Web Api future of Web Service?
For example one of our client needs data from our main database. Normally i use a Web Service for this -simple- purpose but this time i created a Web Api project. I got and service data plus i figured out how it works with Entity or Identity etc. But it's not simple as a web service. I think our client will think same thing also because of identity thing. So why should i prefer Web Api vs Web Service or should i prefer Web Api in this -simple- case?

This kind of depends what you mean by 'web service', but for now I'm going to assume you mean the old .net SOAP services.
If you are building something new today (September 2015) you are almost certainly better off using an asp.net web API. This is a standard REST-style service which can be called by almost any HTTP enabled client with no requirements for local software or understanding of how the service works, this is the whole point of the REST architectural style. I blogged a little about web API and REST here: http://blogs.msdn.com/b/martinkearn/archive/2015/01/05/introduction-to-rest-and-net-web-api.aspx
In your case of a simple service that adds CRUD operations to a database using entity framework. This can be very easily achieved with Web API. You can actually scaffold this whole thing based on a simple model.
To answer your specific question, Yes I believe that in eth asp.net world at least, web API is the future of web services. In fact web services have now been dropped in favour of web API.
Web API supports the .net identity model (I blogged on this here: http://blogs.msdn.com/b/martinkearn/archive/2015/03/25/securing-and-working-securely-with-web-api.aspx) and entity framework.
Hope this helps, if it does please mark as an answer or let me know of any more details you need.

public class Service1 : System.Web.Services.WebService
{
private List<string> GetLines(string filename) {
List<string> lines = new List<string>();
//filename: ime fajla (valute.txt) SA EXT
using (StreamReader sr = new StreamReader(Server.MapPath("podaci/" + filename))) {
string line;
while ((line = sr.ReadLine()) != null) {
lines.Add(line);
}
}
return lines;
}
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[WebMethod]
public double ProcitajKursNaDan(DateTime datum, string valuta) {
List<string> podaci = GetLines("valute.txt");
double kurs = 0.0;
// Pronalazenje upisa
for (int i = 0; i < podaci.Count; i++) {
string[] linija = podaci[i].Split('|');
/* Датум[0] | Oznaka valute[1] | Kurs[2] */
string dat = linija[0];
string val = linija[1];
string vrednost = linija[2];
// Uklanjanje viska
dat = dat.Trim();
val = val.Trim();
vrednost = vrednost.Trim();
// Konverzija:
DateTime datIzFajla = DateTime.ParseExact(dat, "d/M/yyyy", null);
double kursIzFajla = Convert.ToDouble(vrednost);
if (DateTime.Compare(datIzFajla, datum) == 0 && val == valuta)
kurs = kursIzFajla;
}
return kurs;
}
[WebMethod]
public bool UpisiKursNaDan(DateTime datum, string valuta, double Kurs) {
string date = datum.ToString("d/M/yyyy");
string linijaZaUpis = date + " | " + valuta + " | " + Kurs.ToString();
bool success = false;
try
{
StreamWriter sw = new StreamWriter(Server.MapPath("podaci/valute.txt"), true);
sw.WriteLine(linijaZaUpis);
sw.Close();
success = true;
}
catch {
success = false;
}
return success;
}
[WebMethod]
public List<string> ProcitajSveValute() {
List<string> linije = GetLines("valute.txt");
List<string> ValuteIzFajla = new List<string>();
for (int i = 0; i < linije.Count; i++) {
string linija = linije[i];
string valuta = linija.Split('|')[1];
valuta = valuta.Trim();
ValuteIzFajla.Add(valuta);
}
List<string> ValuteKraj = ValuteIzFajla.Distinct().ToList();
return ValuteKraj;
}
}
}
//using A10App.localhost;
//namespace A10App
//{
// public partial class pregledkursa : System.Web.UI.Page
// {
// protected void Page_Load(object sender, EventArgs e)
// {
// if (!this.IsPostBack) {
// Service1 servis = new Service1();
// List<string> valute = servis.ProcitajSveValute().ToList();
// for (int i = 0; i < valute.Count; i++)
// DropDownList1.Items.Add(valute[i]);
// }
// }
// protected void Button1_Click(object sender, EventArgs e)
// {
// string datum = TextBox1.Text;
// string valuta = DropDownList1.Text;
// Service1 servis = new Service1();
// double kurs = servis.ProcitajKursNaDan(DateTime.ParseExact(datum, "d/M/yyyy", null), valuta);
// if (kurs != 0.0)
// Label2.Text = kurs.ToString();
// else
// Label2.Text = "Nije pronadjen kurs";
// }
// }
//}
//namespace A10App
//{
// public partial class azuriranjeliste : System.Web.UI.Page
// {
// protected void Page_Load(object sender, EventArgs e)
// {
// if (!this.IsPostBack)
// {
// Service1 servis = new Service1();
// List<string> valute = servis.ProcitajSveValute().ToList();
// for (int i = 0; i < valute.Count; i++)
// DropDownList1.Items.Add(valute[i]);
// }
// }
// protected void Button1_Click(object sender, EventArgs e)
// {
// string datum = TextBox1.Text;
// string valuta = DropDownList1.Text;
// string kurs = TextBox2.Text;
// Service1 servis = new Service1();
// servis.UpisiKursNaDan(DateTime.ParseExact(datum, "d/M/yyyy", null), valuta, Convert.ToDouble(kurs));
// }
// }
//}

Related

asp.net timer not working

I am new to aspx and can not get my web timer to work. What am I missing here? Also DebugSet.logoutTime = 1800000 and DebugSet.logotWarnings = 3. The user is to be warned every minute before they are logged out of the system. These settings will be raised before the release, I just lowered them for testing purposes.
public partial class test : System.Web.UI.Page
{
private LoggedUser _User;
private Timer LogoutTimer;
private int TmCnt = 0;
protected void Page_Load(object sender, EventArgs e)
{
_User = new LoggedUser(true);
SetTimer();
}
private void SetTimer()
{
LogoutTimer = new Timer();
LogoutTimer.Interval = DebugSet.logoutTime/DebugSet.logoutWarnings;
LogoutTimer.Tick += new EventHandler<EventArgs>(LogoutTimer_Tick);
LogoutTimer.Enabled = true;
LogoutTimer.ViewStateMode = ViewStateMode.Enabled;
}
private void LogoutTimer_Tick(object sender, EventArgs e)
{
TmCnt++;
if (TmCnt == DebugSet.logoutWarnings)
{
_User.UserLoggedIn = false;
_User.SetSessions();
LogoutTimer.Enabled = false;
HttpContext.Current.Session["FCSWarning"] = "LoggedOut";
Response.Redirect("../Views/index.aspx");
}
else
{
int i = (DebugSet.logoutTime / (1000 * 60)) - ((DebugSet.logoutTime / (1000 * 60)) * TmCnt);
string msg = "<Script language=javascript>alert('You will be logged out in " + i.ToString() + " min. due to inactivity.');</Script>";
Response.Write(msg);
}
}
}
The ASP.NET Timer is an ASP.NET control. Each ASP.NET control must be added into a page control hierarchy, otherwise, it won't operate correctly or won't operate at all.
Add your Timer to page control hierarchy:
LogoutTimer = new Timer();
LogoutTimer.ID = "MyTimer";
this.Controls.Add(LogoutTimer);
LogoutTimer.Interval = DebugSet.logoutTime/DebugSet.logoutWarnings;
...
You are using a winforms timer (I think). With websites all instances of variables and classes are destroyed when the page is send to the browser (garbage collection). So LogoutTimer only exists for a very short time. You need to use the Timer control.
https://msdn.microsoft.com/en-us/library/bb386404.aspx
You should know this also when working with websites, the Page Life Cycle:
https://msdn.microsoft.com/en-us/library/ms178472.aspx

user.Roles.FirstOrDefault() returns DynamicProxy

OK...
I was finally making some headway with creating a User Management page using Identity 2 in Web Forms.
It was mostly moving along just fine. When suddenly I run into this issue, and it makes no sense to me.
I have an AS form with a dropdown list of Roles. That list is populated using
roleMgr.Roles.ToList();
Works Great
I use the user being edited Role to set the current selected value.
ddlUserType.SelectedValue = user.Roles.FirstOrDefault().ToString();
This WAS working like dynamite
Last week...
Now all of a sudden user.Roles.FirstOrDefault().ToString(); is returning
"System.Data.Entity.DynamicProxies.IdentityUserRole_FDDE5D267CF62D86904A3BC925D70DC410F12D5BE8313308EC89AC8537DC6375"
What he heck, man?
So I tried user.Roles.Take(1).ToString();
That returns
"System.Linq.Enumerable+d__24`1[Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole]"
I have to presume I Broke, Something...
But What?
Nothing in this code page changed at all between when it worked and then didn't.
The only thing I did related to Identity at all was Migrate a couple of fields into AspNetUsers (another whole ballgame, migrations...) which also worked like dynamite BTW.
I even went to the extreme of wiping out my Migrations and AspNet user tables entirely, and re-initializing it all.
Any suggestions ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Sperry_Parts.Models;
using Sperry_Parts.Logic;
using Microsoft.AspNet.Identity.Owin;
using Owin;
namespace Parts.Admin
{
public partial class CreateEditUser : System.Web.UI.Page
{
private bool NewUser
{
get { return ViewState["NewUser"] != null ? (bool)ViewState["NewUser"] : false; }
set { ViewState["NewUser"] = value; }
}
private string EditUser
{
get { return (string)ViewState["EditUser"]; }
set { ViewState["EditUser"] = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
EditUser = Session["Edit_User"].ToString();
// Access the application context and create result variables.
Models.ApplicationDbContext context = new ApplicationDbContext();
RoleActions roleAction = new RoleActions();
// Create a RoleStore object by using the ApplicationDbContext object.
var roleStore = new RoleStore<IdentityRole>(context);
// Create a RoleManager object that is only allowed to contain IdentityRole objects.
var roleMgr = new RoleManager<IdentityRole>(roleStore);
// Load the DDL of Roles
var roles = roleMgr.Roles.ToList();
ddlUserType.DataTextField = "Name";
ddlUserType.DataValueField = "Id";
ddlUserType.DataSource = roles;
ddlUserType.DataBind();
if (EditUser == "")
{
txtUserName.Enabled = true;
txtUserName.Focus();
NewUser = true;
} // End New User
else
{
// User part
var userMgr = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();
txtUserName.Enabled = false;
txtFullName.Focus();
var user = userMgr.FindByName(EditUser);
if (user != null)
{
txtUserName.Text = user.UserName;
txtUserEmail.Text = user.Email;
txtFullName.Text = user.FullName;
var hisroles = user.Roles.ToList(); // properly returns 1 item
// this is where it went off the rails - these 4 lines are debugging code
string xrole = user.Roles.FirstOrDefault().ToString();
string role2 = user.Roles.Take(1).ToString();
string trythis = xrole.ToString();
string trythis2 = role2.ToString();
// I swear, this worked last week...
ddlUserType.SelectedValue = user.Roles.FirstOrDefault().ToString();
}
} // End Editing User
} // End if (!IsPostBack)
} // End Page Load
protected void CreateUser()
{
// removed as non-relevant to question
} // End CreateUser
protected void UpdateUser()
{
// removed as non-relevant to question
} // End UpdateUser
protected void btnSave_Click(object sender, EventArgs e)
{
// removed as non-relevant to question
} // End btnSave
protected void btnCancel_Click(object sender, EventArgs e)
{
Response.Redirect("~/ManageUsers");
} // End btnCancel
} // End Class CreateEditUser
}
I ran into the same problem trying to set the value of a "Roles" DropDownList inside a GridView Control. I solved it by using:
user.Roles.First().RoleId
It just happens that my RoleId is also my role name.

Get Custom Attribute on ASMX Web Service from HTTP Module

Here's the situation:
Legacy ASP.NET product. A lot of old ASMX services (among other types of endpoints - ASPX, ASHX, etc).
We're enhancing some security logic. Part of the changes dictate defining the application module to which each ASMX service belongs. We plan to use the custom attribute shown below for this purpose.
[AttributeUsage(AttributeTargets.Class)]
public class ModuleAssignmentAttribute : Attribute
{
public Module[] Modules { get; set; }
public ModuleAssignmentAttribute(params Module[] modules)
{
Modules = modules;
}
}
Below is a sample of how the module will be applied to an ASMX service.
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ModuleAssignment(Module.ApplicationModuleA)]
public class SomeService : System.Web.Services.WebService
{
[WebMethod(true)]
public string GetValue()
{
return "Some Service Value";
}
}
The HTTP module below will be used to authorize access to the service.
public class MyAuthorizationModule : IHttpModule
{
public void Dispose()
{
//clean-up code here.
}
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(OnAuthorizeRequest);
}
public void OnAuthorizeRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Handler == null) return;
Attribute att = Attribute.GetCustomAttribute(HttpContext.Current.Handler.GetType(), typeof(ModuleAssignmentAttribute));
if (att != null)
{
Module[] modules = ((ModuleAssignmentAttribute)att).Modules;
// Simulate getting the user's active role ID
int roleId = 1;
// Simulate performing an authz check
AuthorizationAgent agent = new AuthorizationAgent();
bool authorized = agent.AuthorizeRequest(roleId, modules);
if (!authorized)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.StatusCode = 401;
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
}
The problem is that, for ASMX web services, the following line of code from the HTTP module returns null (note that this works for ASPX pages).
Attribute att = Attribute.GetCustomAttribute(HttpContext.Current.Handler.GetType(), typeof(ModuleAssignmentAttribute));
The value of HttpContext.Current.Handler.GetType() in this situation is "System.Web.Script.Services.ScriptHandlerFactory+HandlerWrapperWithSession". That type is apparently unaware of the custom attribute defined on the ASMX service.
Any ideas on how to get the custom attribute from the ASMX service type in this scenario?
Here's a solution to the problem. Requires reflection. Ugly and fragile code - I wouldn't recommend using it if you don't have to. I'd be interested to know if I'm overlooking a more elegant way.
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += new EventHandler(OnAuthorizeRequest);
}
public void OnAuthorizeRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Handler == null) return;
Attribute att = null;
// ScriptHandlerFactory+HandlerWrapperWithSession is the type of handler for ASMX web service calls to web methods that use session.
// This class is internal, so need to do a string comparison here (is there another way?).
if (HttpContext.Current.Handler.GetType().ToString() == "System.Web.Script.Services.ScriptHandlerFactory+HandlerWrapperWithSession")
{
// HandlerWrapperWithSession has a protected field named "_originalHandler" that it inherits from HandlerWrapper.
FieldInfo originalHandlerField = HttpContext.Current.Handler.GetType().GetField("_originalHandler",BindingFlags.NonPublic | BindingFlags.Instance);
object originalHandler = originalHandlerField.GetValue(HttpContext.Current.Handler);
// The _originalHandler value is an instance of SyncSessionHandler.
// The inheritance tree for SyncSessionHandler is:
//
// WebServiceHandler
// ----> SyncSessionlessHandler
// ----> SyncSessionHandler
//
// We need to walk the tree up to the WebServiceHandler class.
bool exitLoop = false;
Type t = originalHandler.GetType();
while (t != null)
{
// WebServiceHandler is internal, so again another string comparison.
if (t.ToString() == "System.Web.Services.Protocols.WebServiceHandler")
{
// WebServiceHandler has a private field named protocol. This field has the type HttpGetServerProtocol.
FieldInfo protocolField = t.GetField("protocol", BindingFlags.NonPublic | BindingFlags.Instance);
object protocolValue = protocolField.GetValue(originalHandler);
// The inheritance tree for ServerProtocol is:
//
// HttpServerProtocol
// ----> HttpGetServerProtocol
//
// We need to walk the three up to the HttpServerProtocol class.
Type t2 = protocolValue.GetType();
while (t2 != null)
{
if (t2.ToString() == "System.Web.Services.Protocols.HttpServerProtocol")
{
// HttpServerProtocol has an internal property named MethodInfo. This property has the type LogicalMethodInfo.
PropertyInfo methodInfoProperty = t2.GetProperty("MethodInfo", BindingFlags.NonPublic | BindingFlags.Instance);
object methodInfoValue = methodInfoProperty.GetValue(protocolValue);
// The LogicalMethodInfo class has a DeclaringType property. This property stores the type of the ASMX service.
att = Attribute.GetCustomAttribute(((LogicalMethodInfo)methodInfoValue).DeclaringType, typeof(ModuleAssignmentAttribute));
exitLoop = true;
break;
}
t2 = t2.BaseType;
}
}
if (exitLoop) break;
t = t.BaseType;
}
}
else
{
att = Attribute.GetCustomAttribute(HttpContext.Current.Handler.GetType(), typeof(ModuleAssignmentAttribute));
}
if (att != null)
{
Module[] modules = ((ModuleAssignmentAttribute)att).Modules;
// Simulate getting the user's active role ID
int roleId = 1;
// Simulate performing an authz check
AuthorizationAgent agent = new AuthorizationAgent();
bool authorized = agent.AuthorizeRequest(roleId, modules);
if (!authorized)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.StatusCode = 401;
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
}
I had to use Reflector + runtime debugging to find this solution.

Web API Return OAuth Token as XML

Using the default Visual Studio 2013 Web API project template with individual user accounts, and posting to the /token endpoint with an Accept header of application/xml, the server still returns the response in JSON:
{"access_token":"...","token_type":"bearer","expires_in":1209599}
Is there a way to get the token back as XML?
According to RFC6749 the response format should be JSON and Microsoft implemented it accordingly. I found out that JSON formatting is implemented in Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerHandler internal class with no means of extension.
I also encountered the need to have token response in XML.
The best solution I came up with was to implement HttpModule converting JSON to XML when stated in Accept header.
public class OAuthTokenXmlResponseHttpModule : IHttpModule
{
private static readonly string FilterKey = typeof(OAuthTokenXmlResponseHttpModule).Name + typeof(MemoryStreamFilter).Name;
public void Init(HttpApplication application)
{
application.BeginRequest += ApplicationOnBeginRequest;
application.EndRequest += ApplicationOnEndRequest;
}
private static void ApplicationOnBeginRequest(object sender, EventArgs eventArgs)
{
var application = (HttpApplication)sender;
if (ShouldConvertToXml(application.Context.Request) == false) return;
var filter = new MemoryStreamFilter(application.Response.Filter);
application.Response.Filter = filter;
application.Context.Items[FilterKey] = filter;
}
private static bool ShouldConvertToXml(HttpRequest request)
{
var isTokenPath = string.Equals("/token", request.Path, StringComparison.InvariantCultureIgnoreCase);
var header = request.Headers["Accept"];
return isTokenPath && (header == "text/xml" || header == "application/xml");
}
private static void ApplicationOnEndRequest(object sender, EventArgs eventArgs)
{
var context = ((HttpApplication) sender).Context;
var filter = context.Items[FilterKey] as MemoryStreamFilter;
if (filter == null) return;
var jsonResponse = filter.ToString();
var xDocument = JsonConvert.DeserializeXNode(jsonResponse, "oauth");
var xmlResponse = xDocument.ToString(SaveOptions.DisableFormatting);
WriteResponse(context.Response, xmlResponse);
}
private static void WriteResponse(HttpResponse response, string xmlResponse)
{
response.Clear();
response.ContentType = "application/xml;charset=UTF-8";
response.Write(xmlResponse);
}
public void Dispose()
{
}
}
public class MemoryStreamFilter : Stream
{
private readonly Stream _stream;
private readonly MemoryStream _memoryStream = new MemoryStream();
public MemoryStreamFilter(Stream stream)
{
_stream = stream;
}
public override void Flush()
{
_stream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _stream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
_memoryStream.Write(buffer, offset, count);
_stream.Write(buffer, offset, count);
}
public override string ToString()
{
return Encoding.UTF8.GetString(_memoryStream.ToArray());
}
#region Rest of the overrides
public override bool CanRead
{
get { throw new NotImplementedException(); }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { throw new NotImplementedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
#endregion
}
Ok I had such a fun time trying to figure this out using OWIN I thought I would share my solution with the community, I borrowed some insight from other posts https://stackoverflow.com/a/26216511/1148288 and https://stackoverflow.com/a/29105880/1148288 along with the concepts Alexei describs in his post. Nothing fancy doing with implementation but I had a requirement for my STS to return an XML formatted response, I wanted to keep with the paradigm of honoring the Accept header, so my end point would examine that to determine if it needed to run the XML swap or not. This is what I am current using:
private void ConfigureXMLResponseSwap(IAppBuilder app)
{
app.Use(async (context, next) =>
{
if (context.Request != null &&
context.Request.Headers != null &&
context.Request.Headers.ContainsKey("Accept") &&
context.Request.Headers.Get("Accept").Contains("xml"))
{
//Set a reference to the original body stream
using (var stream = context.Response.Body)
{
//New up and set the response body as a memory stream which implements the ability to read and set length
using (var buffer = new MemoryStream())
{
context.Response.Body = buffer;
//Allow other middlewares to process
await next.Invoke();
//On the way out, reset the buffer and read the response body into a string
buffer.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(buffer))
{
string responsebody = await reader.ReadToEndAsync();
//Using our responsebody string, parse out the XML and add a declaration
var xmlVersion = JsonConvert.DeserializeXNode(responsebody, "oauth");
xmlVersion.Declaration = new XDeclaration("1.0", "UTF-8", "yes");
//Convert the XML to a byte array
var bytes = Encoding.UTF8.GetBytes(xmlVersion.Declaration + xmlVersion.ToString());
//Clear the buffer bits and write out our new byte array
buffer.SetLength(0);
buffer.Write(bytes, 0, bytes.Length);
buffer.Seek(0, SeekOrigin.Begin);
//Set the content length to the new buffer length and the type to an xml type
context.Response.ContentLength = buffer.Length;
context.Response.ContentType = "application/xml;charset=UTF-8";
//Copy our memory stream buffer to the output stream for the client application
await buffer.CopyToAsync(stream);
}
}
}
}
else
await next.Invoke();
});
}
Of course you would then wire this up during startup config like so:
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
//Highly recommend this is first...
ConfigureXMLResponseSwap(app);
...more config stuff...
}
Hope that helps any other lost souls that find there way to the this post seeking to do something like this!
take a look here i hope it can help how to set a Web API REST service to always return XML not JSON
Could you retry by doing the following steps:
In the WebApiConfig.Register(), specify
config.Formatters.XmlFormatter.UseXmlSerializer = true;
var supportedMediaTypes = config.Formatters.XmlFormatter.SupportedMediaTypes;
if (supportedMediaTypes.Any(it => it.MediaType.IndexOf("application/xml", StringComparison.InvariantCultureIgnoreCase) >= 0) ==false)
{
supportedMediaTypes.Insert(0,new MediaTypeHeaderValue("application/xml"));
}
I normally just remove the XmlFormatter altogether.
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
Add the line above in your WebApiConfig class...
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

Page generation time - ASP.Net MVC

I'm looking for a way to track how long it took for a page to be generated by the server. I know I can use Trace to track this but I need a way to display this per page.
Its ASP.Net MVC 2
You can implement it like a ActionFilterAttribute
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class LoggingAttribute : ActionFilterAttribute
{
private readonly Stopwatch _sw;
public LoggingAttribute()
{
_sw = new Stopwatch();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_sw.Start();
Debug.WriteLine("Beginning executing: " + GetControllerAndActionName(filterContext.ActionDescriptor));
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
_sw.Stop();
var ms = _sw.ElapsedMilliseconds;
Debug.WriteLine("Finishing executing: " + GetControllerAndActionName(filterContext.ActionDescriptor));
Debug.WriteLine("Time elapsed: "+ TimeSpan.FromMilliseconds(ms).TotalSeconds);
}
private string GetControllerAndActionName(ActionDescriptor actionDescriptor)
{
return actionDescriptor.ControllerDescriptor.ControllerName + " - " + actionDescriptor.ActionName;
}
}
Decorate every controller or action-method with it and voila, it spit outs the text in debug.
EDIT: If you want to print it on the page you could add this snippet to the OnActionExecuted method
if(filterContext.Result is ViewResult) { //Make sure the request is a ViewResult, ie. a page
((ViewResult) filterContext.Result).ViewData["ExecutionTime"] = ms; //Set the viewdata dictionary
}
Now you have the executiontime saved in ViewData and can access it in the page.. I usually put it in the masterpage like this
<!-- The page took <%= ViewData["ExecutionTime"] %> ms to execute -->
Yep the Derin Suggestion is the standard Way to do it in an ASP.NEt application, i would just suggest add an if so it does not interfere with non-HTML responses:
EDIT: added complete implementation
public class PerformanceMonitorModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += delegate(object sender, EventArgs e)
{
//Set Page Timer Star
HttpContext requestContext = ((HttpApplication)sender).Context;
Stopwatch timer = new Stopwatch();
requestContext.Items["Timer"] = timer;
timer.Start();
};
context.PostRequestHandlerExecute += delegate(object sender, EventArgs e)
{
HttpContext httpContext = ((HttpApplication)sender).Context;
HttpResponse response = httpContext.Response;
Stopwatch timer = (Stopwatch)httpContext.Items["Timer"];
timer.Stop();
// Don't interfere with non-HTML responses
if (response.ContentType == "text/html")
{
double seconds = (double)timer.ElapsedTicks / Stopwatch.Frequency;
string result_time = string.Format("{0:F4} sec ", seconds);
RenderQueriesToResponse(response,result_time);
}
};
}
void RenderQueriesToResponse(HttpResponse response, string result_time)
{
response.Write("<div style=\"margin: 5px; background-color: #FFFF00\"");
response.Write(string.Format("<b>Page Generated in "+ result_time));
response.Write("</div>");
}
public void Dispose() { /* Not needed */ }
}
you can also add some style to it...
And remember to register your Module in WebConfig inside httpModules Section:
<add name="Name" type="namespace, dll"/>
For a Complete Reference about this check the Pro ASP.NET MVC Framework by Steven Sanderson - Chapter 15 - Performance, Monitoring Page Generation Times.
EDIT:(comment #Pino)
Here is the example for my project:
alt text http://www.diarioplus.com/files/pictures/example_performance.JPG
It will depend on where you want to include this information. For example you could write an http handler that will display the render time after the </html> tag:
public class RenderTimeModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += (sender, e) =>
{
var watch = new Stopwatch();
var app = (HttpApplication)sender;
app.Context.Items["Stopwatch"] = watch;
watch.Start();
};
context.EndRequest += (sender, e) =>
{
var app = (HttpApplication)sender;
var watch = (Stopwatch)app.Context.Items["Stopwatch"];
watch.Stop();
var ts = watch.Elapsed;
string elapsedTime = String.Format("{0} ms", ts.TotalMilliseconds);
app.Context.Response.Write(elapsedTime);
};
}
public void Dispose()
{
}
}
If you want to display render time somewhere in the middle of the html page then this render time will not account for the total page render time.

Resources