Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonReaderException error with UseExceptionHandler and postback #1700

Closed
holajan opened this issue Sep 21, 2023 · 7 comments · Fixed by #1705
Closed

JsonReaderException error with UseExceptionHandler and postback #1700

holajan opened this issue Sep 21, 2023 · 7 comments · Fixed by #1705

Comments

@holajan
Copy link
Contributor

holajan commented Sep 21, 2023

I using app.UseExceptionHandler("/error") in my Configure method in Startup class.
"/error" is URL to my DotVVM error page which should be displayed to the user when an error occurs.

When an error occurs if there is no postback, everything is fine and the error page is displayed, but if the error occurs during a postback, Newtonsoft.Json.JsonReaderException error occurs and the page is not displayed.

Newtonsoft.Json.JsonReaderException
HResult=0x80131500
Message=Error reading JObject from JsonReader. Path '', line 0, position 0.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings) in Newtonsoft.Json.Linq\JObject.cs:line 293
at Newtonsoft.Json.Linq.JObject.Parse(String json, JsonLoadSettings settings) in Newtonsoft.Json.Linq\JObject.cs:line 312
at Newtonsoft.Json.Linq.JObject.Parse(String json) in Newtonsoft.Json.Linq\JObject.cs:line 306
at DotVVM.Framework.ViewModel.Serialization.DefaultViewModelSerializer.PopulateViewModel(IDotvvmRequestContext context, String serializedPostData) in //src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs:line 315
at DotVVM.Framework.Hosting.DotvvmPresenter.d__29.MoveNext() in /
/src/Framework/Framework/Hosting/DotvvmPresenter.cs:line 213
at DotVVM.Framework.Hosting.DotvvmPresenter.d__29.MoveNext() in //src/Framework/Framework/Hosting/DotvvmPresenter.cs:line 321
at DotVVM.Framework.Hosting.DotvvmPresenter.d__28.MoveNext() in /
/src/Framework/Framework/Hosting/DotvvmPresenter.cs:line 87

This exception was originally thrown at this call stack:
Newtonsoft.Json.Linq.JObject.Load(Newtonsoft.Json.JsonReader, Newtonsoft.Json.Linq.JsonLoadSettings) in JObject.cs
Newtonsoft.Json.Linq.JObject.Parse(string, Newtonsoft.Json.Linq.JsonLoadSettings) in JObject.cs
Newtonsoft.Json.Linq.JObject.Parse(string) in JObject.cs
DotVVM.Framework.ViewModel.Serialization.DefaultViewModelSerializer.PopulateViewModel(DotVVM.Framework.Hosting.IDotvvmRequestContext, string) in DefaultViewModelSerializer.cs
DotVVM.Framework.Hosting.DotvvmPresenter.ProcessRequestCore(DotVVM.Framework.Hosting.IDotvvmRequestContext) in DotvvmPresenter.cs
DotVVM.Framework.Hosting.DotvvmPresenter.ProcessRequestCore(DotVVM.Framework.Hosting.IDotvvmRequestContext) in DotvvmPresenter.cs
DotVVM.Framework.Hosting.DotvvmPresenter.ProcessRequest(DotVVM.Framework.Hosting.IDotvvmRequestContext) in DotvvmPresenter.cs

The error occurs because the parsed json is empty:

Screenshot 2023-09-21 200052

exyi added a commit that referenced this issue Sep 29, 2023
AspNetCore reexecutes the same request with the original POST
method. If a DotVVM page is handling the error, we assumed that
we need to execute a postback in the Error page. The request body
was usually already consumed, and even if wasn't a matching command
wasnt found.

resolves #1700
@exyi
Copy link
Member

exyi commented Sep 29, 2023

Thanks for the report, I have a fix in #1705, I hope we get it released soon in the 4.2. If you'd need backport for 4.1, let me know, this shouldn't be a problem.

In the meantime it's possible to work around it by adding a middleware before UseDotvvm which will change the request type to GET if an error occurs. Something like

app.Use(async (context, next) =>
{
    try { return await next(); }
    catch {
       context.Request.Method = "GET";
       throw;
     }
});

@holajan
Copy link
Contributor Author

holajan commented Sep 29, 2023

Hi, thanks
In 4.2 it will be fine.

But, if I use workaround, I now get 403 Forbidden response with:

request rejected: Pages can not be loaded using Javascript for security reasons.
Try refreshing the page to get rid of the error.
If you are the developer, you can disable this check by setting DotvvmConfiguration.Security.VerifySecFetchForPages.DisableForRoute("Error"). [dest: empty, site: same-origin]

Can you help with this?

@exyi
Copy link
Member

exyi commented Sep 29, 2023

Yea sorry, you have to enable JS request to the Error page by putting config.Security.VerifySecFetchForPages.DisableForRoute("Error") into your DotvvmStartup (there isn't anything unsafe about it, the Sec-Fetch validation is only defense in depth against using XSS to scrape content from other pages. I'd not care about someone scraping the error page, even if I had the XSS hole in the app 😁)

@holajan
Copy link
Contributor Author

holajan commented Sep 29, 2023

There is no DisableForRoute method, but I trying configuration.Security.VerifySecFetchForPages.Enabled = false.
Now request returns HTML of my error page, but page is still not displayed in browser.

In F12 console is:
DotVVMErr1

If it's a problem, I can wait for the 4.2, if it works there.

@exyi
Copy link
Member

exyi commented Sep 29, 2023

Sorry, that's our error, the DisableForRoute method is missing in 4.1. You could use EnableForAllRoutesExcept (or just disable it globally as you did).

The page will only be displayed automatically in dotvvm Debug mode, otherwise you unfortunately have to define your own handler of the dotvvm.events.error event. To execute a command, we perform a JS fetch request, so the browser does not automatically show the error page and there isn't a way to "redirect" into the error page. The DotVVM.Debug script shows it in an iframe in full-screen overlay, which is quite sketchy and so it isn't a default for production apps. You can use the a similar logic, see https://github.com/riganti/dotvvm/blob/main/src/Framework/Framework/Resources/Scripts/DotVVM.Debug.js#L75. I'd suggest showing a CSS modal dialog, as users will understand they can close it and retry the command.

@holajan
Copy link
Contributor Author

holajan commented Sep 29, 2023

OK, I understand.
I don't need user to close the error message, so I don't need modal dialog or iframe to display page.
I guess I must do someting like this on client side on error handler:

document.open();
document.write(jqXHR.responseText);
document.close();

Thanks a lot, I'll try that.

exyi added a commit that referenced this issue Sep 29, 2023
AspNetCore reexecutes the same request with the original POST
method. If a DotVVM page is handling the error, we assumed that
we need to execute a postback in the Error page. The request body
was usually already consumed, and even if wasn't a matching command
wasnt found.

resolves #1700
@holajan
Copy link
Contributor Author

holajan commented Oct 12, 2023

OK, I understand. I don't need user to close the error message, so I don't need modal dialog or iframe to display page. I guess I must do someting like this on client side on error handler:

document.open();
document.write(jqXHR.responseText);
document.close();

Thanks a lot, I'll try that.

I end up with this code for display my error page:

dotvvm.events.error.subscribe(function (e) {
    if (e.response && !e.response.bodyUsed) {
        var contentType = e.response.headers.get("Content-Type");
        if ((contentType || "").indexOf("text/html") >= 0) {
            e.response.text().then(function (text) {
                document.open();
                document.write(text);
                document.close();
            });
            e.handled = true;
        }
    }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants