WebDavServer icon indicating copy to clipboard operation
WebDavServer copied to clipboard

How to propagate exceptions after a lock is in place?

Open rliberoff opened this issue 5 years ago • 9 comments

Hi,

We are facing an issue in our project, when We need to throw and exception during a PutAsync action that is called just after the LockAsync action. However, even when the exception is bubbled up and it is a type not handled by the WebDavExceptionFilter, the consumer or client of the WebDav server does not receives the exception. By the way, the UnlockAsync action is always called, regardless the exception we are throwing.

On the other hand, if the exception is throw during the PutAsync action but before the LockAsync, then the consumer receives the exception.

Can you please tell us how could we propagate the the caller or consumer an exception type thrown from an action just after the lock?

Thank you very much in advance.

Regards,

Rodrigo

rliberoff avatar Mar 06 '19 13:03 rliberoff

Sorry, I'm not really sure what you mean. There are either exceptions handled by ASP.NET Core itself and those handled by the custom IExceptionFilter implementation (WebDavExceptionFilter), which handles the following exceptions:

  • NotImplementedException resulting in a HTTP 501 response
  • NotSupportedException resulting in a HTTP 501 response
  • WebDavException resulting in either:
    • HTTP 304 response
    • WebDAV status code + multistatus response
  • UnauthorizedAccessException resulting in a WebDAV status code + multistatus response

I suggest that you implement your own IExceptionFilter to handle all other kinds of exceptions by returning a multistatus response and the correct HTTP status code.

EDIT: Clarification

fubar-coder avatar Mar 07 '19 12:03 fubar-coder

Hi,

It's OK.

The issue we are facing is that we are throwing an exception in the PutAsync action that is called just after the LockAsync action; but this exception is not catch by the application that interacts with the WebDav, it assumes that the action happened correctly. However, Windows shows us a dialog that looks like this:

image

Any idea of how could we circunvent the windows dialog and catch the exception on the consumer application of the WebDav?

Thank you.

Kind regards,

Rodrigo

rliberoff avatar Mar 07 '19 14:03 rliberoff

What kind of exception do you get?

fubar-coder avatar Mar 08 '19 09:03 fubar-coder

Hi,

That's the point. The consumer application of the WebDav is not catching any exception; it continues as if the WebDav has successfully added/copied the file it is sending, however the dialog I copied in my previous message appears due to the exception that is being thrown from the PutAsync.

We throw this exception on purpose, and We would like to have it being capture by the consumer application of the WebDav, but so far it has not been possible, and the only thing that tells us that it actually happen (beyond knowing its existence because we code it) is that dialog from the operative system.

The exception type We're throwing on purpose from the PutAsync is a System.IO.IOException.

Thank you.

Kind regards,

Rodrigo

rliberoff avatar Mar 08 '19 09:03 rliberoff

Aha, so it's an IOException, that was the missing piece. Then I suggest that you implement and register your own IExceptionFilter and return the correct HTTP status code and the corresponding multistatus response.

fubar-coder avatar Mar 08 '19 09:03 fubar-coder

We tried that, and it didn't work.

Our suspicion is that the operative system (Windows) is swallowing the exception and showing the dialog form a previous comment, and that is why the consumer application is not catching it.

But it is a suspicion, and would like another opinion, specially from the creator of the library and your experience with the WebDav protocol.

rliberoff avatar Mar 08 '19 09:03 rliberoff

Hi,

Do you have an example on how to wrap an exception within a multistatus (HTTP 207) response to get a message more or less like this one? -->

<?xml version="1.0"?>
<a:multistatus
  xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/"
  xmlns:a="DAV:">
 <a:response>
   <a:href>http://server/public/test2/item1.txt</a:href>
   <a:propstat>
    <a:status>HTTP/1.1 500 Server Error</a:status>
       <a:prop>
        <a:getcontenttype>text/plain</a:getcontenttype>
        <a:getcontentlength b:dt="int">33</a:getcontentlength>
       </a:prop>
   </a:propstat>
 </a:response>
</a:multistatus>

Thank you.

Kind regards,

Rodrigo

rliberoff avatar Mar 08 '19 11:03 rliberoff

Hi again,

Well, I think We found the way to create the multistatus but it didn't work either.

This is our custom IExceptionFilter:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using FubarDev.WebDavServer;
using FubarDev.WebDavServer.AspNetCore;
using FubarDev.WebDavServer.Model;

using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

using Microsoft.Extensions.DependencyInjection;

namespace TestWebDav
{
    public class TestWebDavExceptionFilter : IExceptionFilter
    {
        public void OnException(ExceptionContext context)
        {
            if (context.ExceptionHandled)
            {
                return;
            }

            if (context.Exception is WebDavException webDavException)
            {
                return;
            }

            context.Result = BuildResultForStatusCode(context, WebDavStatusCode.BadGateway, context.Exception.Message);
        }

        private static IActionResult BuildResultForStatusCode(ExceptionContext context, WebDavStatusCode statusCode, string message)
        {
            var result = new WebDavResult<multistatus>(
                statusCode,
                new multistatus()
                {
                    response = new[]
                    {
                        new response()
                        {
                            href = context.HttpContext.Request.GetEncodedUrl(),
                            ItemsElementName = new[] { ItemsChoiceType2.status, },
                            Items = new object[] { new Status(context.HttpContext.Request.Protocol, statusCode, message).ToString() },
                        },
                    },
                });

            var dispatcher = context.HttpContext.RequestServices.GetService<IWebDavDispatcher>();

            return new WebDavIndirectResult(dispatcher, result, null);
        }
    }
}

This works as expected, however Windows still shows us the same dialog.

In any case, thanks for the help.

Kind regards,

Rodrigo

rliberoff avatar Mar 08 '19 11:03 rliberoff

The changes you made are also available in the release/2.0 branch, but I used an ExceptionFilterAttribute instead of the IExceptionFilter.

I'll leave this ticket open, because maybe we need to return the error element too, together with an element that's a Microsoft WebDAV extension?

fubar-coder avatar Nov 22 '21 08:11 fubar-coder