I have a asp.net client web application and a WCF web service which was developed from schema xsd. When calling the service i get an error in deserializing body of request. I tried updating service reference but that did not help.
This is my code:
OSEOP.HMA_OrderingBindingClient client = new OSEOP.HMA_OrderingBindingClient();
OSEOP.GetCapabilitiesRequest request = new OSEOP.GetCapabilitiesRequest();
request.GetCapabilities = new OSEOP.GetCapabilities();
request.GetCapabilities.service = "OS";
string[] arrAcceptedVersions = { "1.0.0", "2.0.0" };
request.GetCapabilities.AcceptVersions = arrAcceptedVersions;
OSEOP.Capabilities capabilities = client.GetCapabilities(request.GetCapabilities);
txtGetCapabilitiesResponse.Text = capabilities.Contents.ToString();
client.Close();
and this is the error:
System.ServiceModel.FaultException`1 was unhandled by user code
Message=Error in deserializing body of request message for operation 'GetCapabilities'.
Source=mscorlib
StackTrace:
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at OSEOP.HMA_OrderingBinding.GetCapabilities(GetCapabilitiesRequest request)
at OSEOP.HMA_OrderingBindingClient.OSEOP.HMA_OrderingBinding.GetCapabilities(GetCapabilitiesRequest request) in c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\oseop_testclient\023fa9f5\ea876945\App_WebReferences.k9c5tqe1.0.cs:line 44135
at OSEOP.HMA_OrderingBindingClient.GetCapabilities(GetCapabilities GetCapabilities1) in c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\oseop_testclient\023fa9f5\ea876945\App_WebReferences.k9c5tqe1.0.cs:line 44141
at _Default.cmdGetCapabilities_Click(Object sender, EventArgs e) in d:\Documents\DEV\SARPilot\SVN_repository\Services\OrderingServices\TestClient\Default.aspx.cs:line 30
at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
InnerException:
as you can see, the error happens at the client and never gets sent out to the WCF service. For this reason i'm not getting anything in my MessageLogging. That's why i thought it would have something to do with the service reference.
Can anyone help?
EDIT #1:
What i don't understand is the GetCapabilities takes a GetCapabilitiesRequest parameter but when i'm implementing the client, my intellisense asks for a OSEOP.GetCapabilities object.
OSEOP is what i named the web reference.
public class OrderingService : HMA_OrderingBinding
{
public GetCapabilitiesResponse GetCapabilities(GetCapabilitiesRequest request)
{
throw new NotImplementedException();
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://www.opengis.net/oseop/1.0", ConfigurationName = "HMA_OrderingBinding")]
public interface HMA_OrderingBinding
{
[OperationContract]
[XmlSerializerFormatAttribute]
GetCapabilitiesResponse GetCapabilities(GetCapabilitiesRequest request);
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.2152")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.opengis.net/oseop/1.0")]
public partial class Capabilities : CapabilitiesBaseType
{
private OrderingServiceContentsType contentsField;
private NotificationProducerMetadataPropertyType notificationsField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order = 0)]
public OrderingServiceContentsType Contents
{
get
{
return this.contentsField;
}
set
{
this.contentsField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Order = 1)]
public NotificationProducerMetadataPropertyType Notifications
{
get
{
return this.notificationsField;
}
set
{
this.notificationsField = value;
}
}
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.MessageContractAttribute(IsWrapped = false)]
public partial class GetCapabilitiesRequest
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace = "http://www.opengis.net/oseop/1.0", Order = 0)]
public GetCapabilities GetCapabilities;
public GetCapabilitiesRequest()
{
}
public GetCapabilitiesRequest(GetCapabilities GetCapabilities)
{
this.GetCapabilities = GetCapabilities;
}
}
EDIT #2 #Marc:
Marc, your answer was very helpful. But you see how the server side is something like this:
GetCapabilitiesResponse GetCapabilities(GetCapabilitiesRequest request)
Yet my intellisense thinks it's something like this:
Capabilities GetCapabilities(GetCapabilities GetCapabilities1)
And I've found a snippet of code within the IOrder.cs file (47,256 lines of code generated from schema) that I'm sure is causing the problem but I tried commenting out the trouble function, updating service reference, and my intellisense still wants GetCapabilities GetCapabilities1
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class HMA_OrderingBindingClient : System.ServiceModel.ClientBase<HMA_OrderingBinding>, HMA_OrderingBinding
{
public HMA_OrderingBindingClient()
{
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
GetCapabilitiesResponse HMA_OrderingBinding.GetCapabilities(GetCapabilitiesRequest request)
{
return base.Channel.GetCapabilities(request);
}
public Capabilities GetCapabilities(GetCapabilities GetCapabilities1)
{
GetCapabilitiesRequest inValue = new GetCapabilitiesRequest();
inValue.GetCapabilities = GetCapabilities1;
GetCapabilitiesResponse retVal = ((HMA_OrderingBinding)(this)).GetCapabilities(inValue);
return retVal.Capabilities;
}
}
Two questions:
Why do you create a GetCapabilitiesRequest object which contains a subobject GetCapabilities, and then in your method call, you only use the contained suboject GetCapabilities??
So why not just create the GetCapabilities in the first place and forget about the wrapping object??
Also, can you please show us the GetCapabilitiesRequest and GetCapabilities and the return class Capabilities, too? If you have a deserialization error, most likely something with those classes isn't right...
Update: thanks for the update to your question....
hmm... can't seem to find anything obviously wrong at first glance....
About your question:
What I don't understand is the
GetCapabilities takes a
GetCapabilitiesRequest parameter but
when I'm implementing the client, my
intellisense asks for a
OSEOP.GetCapabilities object.
Yes, that's clear - your service-side uses its set of classes - GetCapabilitiesRequest and so forth.
When you do an Add Service Reference in Visual Studio, what VS does is
interrogate the server to find out about the service - what methods it has and what parameters it needs
it creates a set of copies of your classes for the client-side proxy - in that namespace that you define on the Add Service Reference dialog box. Those are classes that look exactly the same as your server side classes - but they are not the same classes - they just serialize to XML (and deserialize from XML) the same way as those on the server. That's why your client-side proxy has different classes in a different namespace. That's standard WCF behavior - nothing to be alarmed about...
Update no. 2: Carlos, the schema you sent me seems to be incomplete or has errors. Try to use OGC project on CodePlex as a base and build in your code manually or wait until the schema gets ‘officially’ published.
Related
We are using spring and spring-security-3.2. Recently We are adding annotations #PreAuthorize to RestAPIs(earlier it was URL based).
#PreAuthorize("hasPermission('salesorder','ViewSalesOrder')")
#RequestMapping(value = "/restapi/salesorders/", method = RequestMethod.GET)
public ModelAndView getSalesOrders(){}
We already have Global exception handler which annotated with - #ControllerAdvice and custom PermissionEvaluator in place, everything works fine except the error message.
Lets say some user is accessing API At moment without having 'ViewSalesOrder' permission then spring by default throws the exception 'Access is denied',but didn't tell which permission is missing (Its our requirement to mention which permission is missing).
Is it possible to throw an exception which also include the permission name, so final error message should be look like "Access is denied, you need ViewSalesOrder permission"(here permission name should be from #PreAuthorize annotation)?
Please note that we have 100 such restAPI in place so generic solution will be highly appreciated.
There is no pretty way of achieving what you expect since PermissionEvaluator interface doesn't let you pass the missing permission along with the result of the evaluation.
In addition, AccessDecisionManager decides on the final authorization with respect to the votes of the AccessDecisionVoter instances, one of which is PreInvocationAuthorizationAdviceVoter which votes with respect to the evaluation of #PreAuthorize value.
Long story short, PreInvocationAuthorizationAdviceVoter votes against the request (giving the request –1 point) when your custom PermissionEvaluator returns false to hasPermission call. As you see there is no way to propagate the cause of the failure in this flow.
On the other hand, you may try some workarounds to achieve what you want. One way can be to throw an exception within your custom PermissionEvaluator when permission check fails. You can use this exception to propagate the missing permission to your global exception handler. There, you can pass the missing permission to your message descriptors as a parameter. Beware that this will halt execution process of AccessDecisionManager which means successive voters will not be executed (defaults are RoleVoter and AuthenticatedVoter). You should be careful if you choose to go down this path.
Another safer but clumsier way can be to implement a custom AccessDeniedHandler and customize the error message before responding with 403. AccessDeniedHandler provides you current HttpServletRequest which can be used to retrieve the request URI. However, bad news in this case is, you need a URI to permission mapping in order to locate the missing permission.
I have implemented the second possible solution mentioned by Mert Z. My solution works only for #PreAuthorize annotations used in the API layer (e.g. with #RequestMapping). I have registered a custom AccessDeniedHandler bean in which I get the value of the #PreAuthorize annotation of the forbidden API method and fills it into error message.
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private DispatcherServlet dispatcherServlet;
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
if (!response.isCommitted()) {
List<HandlerMapping> handlerMappings = dispatcherServlet.getHandlerMappings();
if (handlerMappings != null) {
HandlerExecutionChain handler = null;
for (HandlerMapping handlerMapping : handlerMappings) {
try {
handler = handlerMapping.getHandler(request);
} catch (Exception e) {}
if (handler != null)
break;
}
if (handler != null && handler.getHandler() instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler.getHandler();
PreAuthorize methodAnnotation = method.getMethodAnnotation(PreAuthorize.class);
if (methodAnnotation != null) {
response.sendError(HttpStatus.FORBIDDEN.value(),
"Authorization condition not met: " + methodAnnotation.value());
return;
}
}
}
response.sendError(HttpStatus.FORBIDDEN.value(),
HttpStatus.FORBIDDEN.getReasonPhrase());
}
}
#Inject
public void setDispatcherServlet(DispatcherServlet dispatcherServlet) {
this.dispatcherServlet = dispatcherServlet;
}
}
The handler is registered in WebSecurityConfigurerAdapter:
#EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
#EnableWebSecurity
public abstract class BaseSecurityInitializer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
...
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
}
Beware that if there is also a global resource exception handler with #ControllerAdvice the CustomAccessDeniedHandler won't be executed. I solved this by rethrowing the exception in the global handler (as advised here https://github.com/spring-projects/spring-security/issues/6908):
#ControllerAdvice
public class ResourceExceptionHandler {
#ExceptionHandler(AccessDeniedException.class)
public ResponseEntity accessDeniedException(AccessDeniedException e) throws AccessDeniedException {
log.info(e.toString());
throw e;
}
}
You can throw an org.springframework.security.access.AccessDeniedException from a method that was called inside an EL-Expression:
#PreAuthorize("#myBean.myMethod(#myRequestParameter)")
Ideally, the #PreAuthorize annotation should be supporting String message(); in addition to the SpEl value. But, for whatever reason, it does not. Most of the suggestions here seem unnecessarily cumbersome and elaborate. As #lathspell has suggested, the simplest way to provide your own error message - along with any custom access validation logic - would be to add a simple method that performs the check and throws the AccessDeniedException in case the check fails, and then reference that method in the SpEl expression. Here's an example:
#RestController
#RequiredArgsConstructor // if you use lombok
public class OrderController {
private final OrderService orderService;
...
#GetMapping(value = "/salesorders", produces = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("#orderController.hasPermissionToSeeOrders(#someArgOfThisMethod)")
public Page<OrderDto> getSalesOrders(
// someArgOfThisMethod here, perhaps HttpRequest, #PathVariable, #RequestParam, etc.
int pageIndex, int pageSize, String sortBy, String sortOrder) {
Pageable pageRequest = PageRequest.of(pageIndex, pageSize, Sort.Direction.fromString(sortOrder), sortBy);
return ordersService.retrieveSalesOrders(..., pageRequest);
}
public static Boolean hasPermissionToSeeOrders(SomeArgOfTheTargetMethod argToEvaluate) {
//check eligibility to perform the operation based on some data from the incoming objects (argToEvaluate)
if (condition fails) {
throw new AccessDeniedException("Your message");
}
return true;
}
I was trying to verify whether my log warning message is written via NUnit mocking. I am getting this error message :
An exception of type 'System.NotSupportedException' occurred in Moq.dll but was not handled in user code
Additional information: Invalid verify on a non-virtual (overridable in VB) member: m => m.LogWarning(String.Format("comments not found for part number :{0}", (Object)0), new[] { "111" })
code:
mockLogger.Verify(m => m.LogWarning($"comments not found for part number :{0}", "111"), Times.Exactly(1));
This is happening because NUnit mocking framework does not support extension methods. A few people on stack overflow have suggested to use Log method instead of level wise methods.
What am I missing?
Firstly, you don't need the $ at the start of the string. That's for string interpolation. The LogWarning message is doing a string.format, hence the {0}
Mock frameworks cannot directly mock static methods. The problem in your case is the LogWarning method - that is the static (extension) method.
The simplest way of overcoming this issue is by using a wrapper class. Here's how I got it, in your case.
Firstly I created an interface
public interface IMyLogWarning
{
void LogWarning(string msg, params object[] args);
}
Then I created a class which implements that interface
public class MyLogWarning<T> : IMyLogWarning where T : class
{
private readonly ILogger _logger;
public MyLogWarning(ILogger<T> logger)
{
// Using constructor for DI
_logger = logger;
}
public void LogWarning(string msg, params object[] args)
{
_logger.LogWarning(msg, args);
}
}
The reason for these two is that I'll use these in my code as well as the unit test.
The constructor in the class is setup so it can be populated using dependency injection, something like this in your ConfigureServices method. Feel free to change this; was a quick stab at it on my part.
services.AddTransient<IMyLogWarning, MyLogWarning<MyViewModel>>();
You can then create a unit test that's roughly like this
[Test]
public void LoggingTest_LogAMessage_ConfirmedLogWasRun()
{
// TODO - add the rest of your test code
// Arrange
var warningMsg = "comments not found for part number :{0}";
var partNumber = "111";
var mockLogger = new Mock<IMyLogWarning>();
// Act
mockLogger.Object.LogWarning(warningMsg, partNumber);
// Assert
mockLogger.Verify(m => m.LogWarning(warningMsg, partNumber), Times.Exactly(1));
}
I have an ASP.NET Web Forms application with UI, Service layer and Repository layer.
Some of the methods in my Service Layer communicates with a Web Service, therefore I would like to wrap all the calls to the Web Methods in a Try-Catch-Finally construct.
Suppose I have the following methods in my Service Layer:
public RegistrationDetails GetRegistrationDetails(int userId)
public bool RegisterUser(UserData myUserData)
Where RegistrationDetails and myUserData are object types (classes).
My concern is the following: if I create a Try-Catch-Finally to wrap the call to the Web Service within the implementation of the methods listed above, in case there is an exception how can I return the message string if the return types are RegistrationDetails and bool?
I was thinking about adding a property to every return object but I do not know if that is a good solution. For instance instead of using bool:
public class RegisterResponse
{
public bool isRegistered { get; set; }
public string ExceptionMessage { get; set; }
}
public RegisterResponse RegisterUser(UserData myUserData)
And then check if ExceptionMessage is null or String.Empty. Is it a good approach? Thanks
1) As mentioned by IrishChieftain, bubbling the exception down to the forms is good, you will be able to respond to the exception better
2) You can also have a reference parameter as array which stores exception messages generated from the method
public bool RegisterUser(UserData myUserData, optional ref ArrayList<string> errors)
{
if(error)
errors.Add("This error occured")
}
3) For Instance Object, you could have an Instance variable of ArrayList for errors and have that returned in a property
public class MyClass
{
private ArrayList<string> errors = new ArrayList<string>
public ArrayList<string> ExceptionMessages()
{
get
{
return errors;
}
}
public RegistrationDetails GetRegistrationDetails(int userId) { }
}
//Used like this:
MyClass c = new MyClass();
c.GetRegistrationDetails();
if(c.ExceptionMessages > 0)
{
//output errors
}
But I would prefer the first approach - for flexibility like output formatting
Passing raw exceptions on to your client (web forms layer) from a service could be risky. If it's a database exception, it might expose details of your database. A malicious user might call your service layer methods from their own application.
You can expect two types of exceptions on the client level:
Communication Exception (problems connecting to the service)
Server-side error (database problem, username not unique, password invalid... other business rule exceptions)
The first type should be caught in try-catch-finally in your web forms layer, but the second kind should be caught in the service layer, logged, then wrapped up in the RegisterResponse object, as you suggest. But, instead of sending Exception.Message, you might consider using an enum of expected errors (with a ServerError member to cover anything else.) You could also add an EventId to the response and log entry so that you can investigate errors.
public enum RegisterResponseError { NoError = 0, SystemError = 1,
UserNameNotUnique, PasswordInvalid, etc. }
public class RegisterResponse
{
public bool isRegistered { get; set; }
public RegisterResponseError ErrorCode { get; set; }
}
Then in your client code,
if(myRegisterResponse.ErrorCode == RegisterResponseError.NoError)
// everything was fine
else
// show a suitable error message for ErrorCode and display EventId (if logging)
You could return an error string from your service, but it's probably better to manage any content in your web forms layer, in case you need to localize the content or use a CMS later on.
I want to write a unit test that verifies my route registration and ControllerFactory so that given a specific URL, a specific controller will be created. Something like this:
Assert.UrlMapsToController("~/Home/Index",typeof(HomeController));
I've modified code taken from the book "Pro ASP.NET MVC 3 Framework", and it seems it would be perfect except that the ControllerFactory.CreateController() call throws an InvalidOperationException and says This method cannot be called during the application's pre-start initialization stage.
So then I downloaded the MVC source code and debugged into it, looking for the source of the problem. It originates from the ControllerFactory looking for all referenced assemblies - so that it can locate potential controllers. Somewhere in the CreateController call-stack, the specific trouble-maker call is this:
internal sealed class BuildManagerWrapper : IBuildManager {
//...
ICollection IBuildManager.GetReferencedAssemblies() {
// This bails with InvalidOperationException with the message
// "This method cannot be called during the application's pre-start
// initialization stage."
return BuildManager.GetReferencedAssemblies();
}
//...
}
I found a SO commentary on this. I still wonder if there is something that can be manually initialized to make the above code happy. Anyone?
But in the absence of that...I can't help notice that the invocation comes from an implementation of IBuildManager. I explored the possibility of injecting my own IBuildManager, but I ran into the following problems:
IBuildManager is marked internal, so I need some other authorized derivation from it. It turns out that the assembly System.Web.Mvc.Test has a class called MockBuildManager, designed for test scenarios, which is perfect!!! This leads to the second problem.
The MVC distributable, near as I can tell, does not come with the System.Web.Mvc.Test assembly (DOH!).
Even if the MVC distributable did come with the System.Web.Mvc.Test assembly, having an instance of MockBuildManager is only half the solution. It is also necessary to feed that instance into the DefaultControllerFactory. Unfortunately the property setter to accomplish this is also marked internal (DOH!).
In short, unless I find another way to "initialize" the MVC framework, my options now are to either:
COMPLETELY duplicate the source code for DefaultControllerFactory and its dependencies, so that I can bypass the original GetReferencedAssemblies() issue. (ugh!)
COMPLETELY replace the MVC distributable with my own build of MVC, based on the MVC source code - with just a couple internal modifiers removed. (double ugh!)
Incidentally, I know that the MvcContrib "TestHelper" has the appearance of accomplishing my goal, but I think it is merely using reflection to find the controller - rather than using the actual IControllerFactory to retrieve a controller type / instance.
A big reason why I want this test capability is that I have made a custom controller factory, based on DefaultControllerFactory, whose behavior I want to verify.
I'm not quite sure what you're trying to accomplish here. If it's just testing your route setup; you're way better off just testing THAT instead of hacking your way into internals. 1st rule of TDD: only test the code you wrote (and in this case that's the routing setup, not the actual route resolving technique done by MVC).
There are tons of posts/blogs about testing a route setup (just google for 'mvc test route'). It all comes down to mocking a request in a httpcontext and calling GetRouteData.
If you really need some ninja skills to mock the buildmanager: there's a way around internal interfaces, which I use for (LinqPad) experimental tests. Most .net assemblies nowadays have the InternalsVisibleToAttribute set, most likely pointing to another signed test assembly. By scanning the target assembly for this attribute and creating an assembly on the fly that matches the name (and the public key token) you can easily access internals.
Mind you that I personally would not use this technique in production test code; but it's a nice way to isolate some complex ideas.
void Main()
{
var bm = BuildManagerMockBase.CreateMock<MyBuildManager>();
bm.FileExists("IsCool?").Dump();
}
public class MyBuildManager : BuildManagerMockBase
{
public override bool FileExists(string virtualPath) { return true; }
}
public abstract class BuildManagerMockBase
{
public static T CreateMock<T>()
where T : BuildManagerMockBase
{
// Locate the mvc assembly
Assembly mvcAssembly = Assembly.GetAssembly(typeof(Controller));
// Get the type of the buildmanager interface
var buildManagerInterface = mvcAssembly.GetType("System.Web.Mvc.IBuildManager",true);
// Locate the "internals visible to" attribute and create a public key token that matches the one specified.
var internalsVisisbleTo = mvcAssembly.GetCustomAttributes(typeof (InternalsVisibleToAttribute), true).FirstOrDefault() as InternalsVisibleToAttribute;
var publicKeyString = internalsVisisbleTo.AssemblyName.Split("=".ToCharArray())[1];
var publicKey = ToBytes(publicKeyString);
// Create a fake System.Web.Mvc.Test assembly with the public key token set
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "System.Web.Mvc.Test";
assemblyName.SetPublicKey(publicKey);
// Get the domain of our current thread to host the new fake assembly
var domain = Thread.GetDomain();
var assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
moduleBuilder = assemblyBuilder.DefineDynamicModule("System.Web.Mvc.Test", "System.Web.Mvc.Test.dll");
AppDomain currentDom = domain;
currentDom.TypeResolve += ResolveEvent;
// Create a new type that inherits from the provided generic and implements the IBuildManager interface
var typeBuilder = moduleBuilder.DefineType("Cheat", TypeAttributes.NotPublic | TypeAttributes.Class, typeof(T), new Type[] { buildManagerInterface });
Type cheatType = typeBuilder.CreateType();
// Magic!
var ret = Activator.CreateInstance(cheatType) as T;
return ret;
}
private static byte[] ToBytes(string str)
{
List<Byte> bytes = new List<Byte>();
while(str.Length > 0)
{
var bstr = str.Substring(0, 2);
bytes.Add(Convert.ToByte(bstr, 16));
str = str.Substring(2);
}
return bytes.ToArray();
}
private static ModuleBuilder moduleBuilder;
private static Assembly ResolveEvent(Object sender, ResolveEventArgs args)
{
return moduleBuilder.Assembly;
}
public virtual bool FileExists(string virtualPath) { throw new NotImplementedException(); }
public virtual Type GetCompiledType(string virtualPath) { throw new NotImplementedException(); }
public virtual ICollection GetReferencedAssemblies() { throw new NotImplementedException(); }
public virtual Stream ReadCachedFile(string fileName) { throw new NotImplementedException(); }
public virtual Stream CreateCachedFile(string fileName) { throw new NotImplementedException(); }
}
I'm using the [System.Web.Script.Services.ScriptService] tag to use web services callable from client side javascript. What I need is a way of globally logging any unhandled exceptions in those methods. On the client side, I get the error callback and can proceed from there, but I need a server-side catch to log the exception.
The guy at this url:
http://ayende.com/Blog/archive/2008/01/06/ASP.Net-Ajax-Error-Handling-and-WTF.aspx
suggests that this can't be done.
Is that accurate? Do I seriously have to go to every single webmethod in the entire system and try/catch the method as a whole.
You can use an HTTP module to capture the exception message, stack trace and exception type that is thrown by the web service method.
First some background...
If a web service method throws an exception the HTTP response has a status code of 500.
If custom errors are off then the web
service will return the exception
message and stack trace to the client
as JSON. For example:{"Message":"Exception
message","StackTrace":" at
WebApplication.HelloService.HelloWorld()
in C:\Projects\Stackoverflow
Examples\WebApplication\WebApplication\HelloService.asmx.cs:line
22","ExceptionType":"System.ApplicationException"}
When custom errors are on then the
web service returns a default message
to the client and removes the stack
trace and exception type:{"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""}
So what we need to do is set custom errors off for the web service and plug in an HTTP module that:
Checks if the request is for a web service method
Checks if an exception was thrown - that is, a status code of 500 is being returned
If 1) and 2) are true then get the original JSON that would be sent to the client and replace it with the default JSON
The code below is an example of an HTTP module that does this:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
public class ErrorHandlerModule : IHttpModule {
public void Init(HttpApplication context) {
context.PostRequestHandlerExecute += OnPostRequestHandlerExecute;
context.EndRequest += OnEndRequest;
}
static void OnPostRequestHandlerExecute(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
// TODO: Update with the correct check for your application
if (context.Request.Path.StartsWith("/HelloService.asmx")
&& context.Response.StatusCode == 500) {
context.Response.Filter =
new ErrorHandlerFilter(context.Response.Filter);
context.EndRequest += OnEndRequest;
}
}
static void OnEndRequest(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
ErrorHandlerFilter errorHandlerFilter =
context.Response.Filter as ErrorHandlerFilter;
if (errorHandlerFilter == null) {
return;
}
string originalContent =
Encoding.UTF8.GetString(
errorHandlerFilter.OriginalBytesWritten.ToArray());
// If customErrors are Off then originalContent will contain JSON with
// the original exception message, stack trace and exception type.
// TODO: log the exception
}
public void Dispose() { }
}
This module uses the following filter to override the content sent to the client and to store the original bytes (which contain the exception message, stack trace and exception type):
public class ErrorHandlerFilter : Stream {
private readonly Stream _responseFilter;
public List OriginalBytesWritten { get; private set; }
private const string Content =
"{\"Message\":\"There was an error processing the request.\"" +
",\"StackTrace\":\"\",\"ExceptionType\":\"\"}";
public ErrorHandlerFilter(Stream responseFilter) {
_responseFilter = responseFilter;
OriginalBytesWritten = new List();
}
public override void Flush() {
byte[] bytes = Encoding.UTF8.GetBytes(Content);
_responseFilter.Write(bytes, 0, bytes.Length);
_responseFilter.Flush();
}
public override long Seek(long offset, SeekOrigin origin) {
return _responseFilter.Seek(offset, origin);
}
public override void SetLength(long value) {
_responseFilter.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count) {
return _responseFilter.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count) {
for (int i = offset; i < offset + count; i++) {
OriginalBytesWritten.Add(buffer[i]);
}
}
public override bool CanRead {
get { return _responseFilter.CanRead; }
}
public override bool CanSeek {
get { return _responseFilter.CanSeek; }
}
public override bool CanWrite {
get { return _responseFilter.CanWrite; }
}
public override long Length {
get { return _responseFilter.Length; }
}
public override long Position {
get { return _responseFilter.Position; }
set { _responseFilter.Position = value; }
}
}
This method requires custom errors to be switched off for the web services. You would probably want to keep custom errors on for the rest of the application so the web services should be placed in a sub directory. Custom errors can be switched off in that directory only using a web.config that overrides the parent setting.
You run the Stored Procedure in the backend. Then, for a single variable, it returns more than 1 value. Because of that, a conflicts occurs, and, this error is thrown.
I know this doesn't answer the question per-say, but I went on my own quest a while back to find this out and would up empty handed. Ended up wrapping each web service call in a try/catch, and the catch calls our error logger. Sucks, but it works.
In ASP.Net it is possible to catch all run handled exceptions using a global error handler although the blog post suggest this would not work but you could experiment with this approach trying to rethrow the error in some way?
Another idea would be to look at the open source elmah (Error Logging Modules and Handlers) for ASP.Net that might help or someone in that community may have an idea.