Closed TanvirArjel closed 1 year ago
cc @brunolins16
@TanvirArjel I have very happy that you have tried the new ProblemDetailsService 😁.
If you take a look at the same release notes (Diagnostics middleware updates) we mentioned about updates in the middleware to make the response consistent and there we have mentioned a key important aspect of this change.
The following sample configures the app to generate a problem details response for all HTTP client and server error responses that do not have a body content yet.
Basically, it means that once you have a body defined the middleware will not call the problem details service to generate a Problem Details
payload. It is similar to what you have in your scenario, but in your case the MVC engine is responsible to autogenerate the payload when the body is empty.
In your code return BadRequest(ModelState);
call will serialize the ModelStateDictionary
to the body avoid the Problem Details
autogeneration.
In your code return BadRequest(ModelState); call will serialize the ModelStateDictionary to the body avoid the Problem Details autogeneration.
Yes! That's why this behavior is inconsistent and error-prone and the response for the bad requests always should be the same.
With all that said, that is the current expected behavior. However, I can understand your expectation once the service is registered and I think since now we have a service that allow a consistent generation might be an interesting change to generate PD in these scenarios as well,
@brunolins16
I think since now we have a service that allows a consistent generation might be an interesting change to generate PD in these scenarios as well,
Yes! otherwise, the Client App calling the ASP.NET Core API will be broken while handling the bad request error response.
In your code return BadRequest(ModelState); call will serialize the ModelStateDictionary to the body avoid the Problem Details autogeneration.
Yes! That's why this behavior is inconsistent and error-prone and the response for the bad requests always should be the same.
Totally agree! I think we have commented at same time. In my opinion, adding the new service was a start point to make the Problem Details generation consistent, but we still need to make additional changes to allow it to be customizable and consistent in scenarios as you mentioned.
For now, to make it consistent I recommend you keep using return ValidationProblem(ModelState);
@brunolins16 This is a very crucial bug. I am surprised that this very basic requirement is not noticed by ASP.NET Core Team while they are going to release version 7.0 soon.
@brunolins16
For now, to make it consistent I recommend you keep using return ValidationProblem(ModelState);
Thanks! I am aware of this.
@brunolins16
For now, to make it consistent I recommend you keep using return ValidationProblem(ModelState);
Thanks! I am aware of this. But writing it everywhere will be ugly.
I understand you, but I don't see, in your specific example, how uglier it is since it is basically a validation problem already,
@brunolins16 This is a very crucial bug. I am surprised that this very basic requirement is not noticed by ASP.NET Core Team while they are going to release version 7.0 soon.
I was the team member who worked on this and to be honest probably I missed because the main scenario was the auto generation
(when a body was not defined already) that we used to have no control and, in your case, you do have control what you want to generate (calling ValidationProblem/ Problem/etc...), and this current behavior is very well documented
Unfortunately, we are very late in .NET 7 development and might not be possible to have it done :(
@brunolins16 Wish you all the very best for dotNET 8.0 :) But please don't miss this.
@brunolins16
I understand you, but I don't see, in your specific example, how uglier it is since it is basically a validation problem already,
I see! It's a good workaround. Thanks again.
DefaultApiProblemDetailsWriter
also doesn't support the CustomizeProblemDetails
callback which seems somewhat inconsistent--though I suppose you could just customize it at creation time.
But would that writer even be invoked? It seems like the DefaultProblemDetailsWriter
would always return true
from CanWrite
for the cases that one would.... so I guess it depends on the order you add the services.
If you want the behavior you're after you should check out the ProblemDetailsMiddleware nuget which supports that and more. My hope was to migrate off it but I'm not sure it'll be possible as I rely on some of the features that aren't included in the new services
@pinkfloydx33 it does, let me double check and share with you where it is called.
Any writer registered before the call to the AddProblemDetails
will be evaluated before the DefaultProblemDetailsWrapper
It is called here
Ahh my mistake... It's tucked away in the factory; I was looking for it in the writer similar to the default one. Good to know it's there.
Any writer registered before the call to the AddProblemDetails will be evaluated before the DefaultProblemDetailsWrapper
Ok...so if using MVC Controllers (and not minimal API) you'd have to be sure to call AddMvc
/AddControllers
before AddProblemDetails
if you want it to use the code in DefaultApiProblemDetailsWriter
.
FWIW the outputs from the two are mostly the same anyways, though by default DefaultApiProblemDetailsWriter
will add traceId
extension (via the factory) while the DefaultProblemDetailsWriter
won't. A properly constructed customization callback could rectify that (if that's what one wants/expects).
Ok...so if using MVC Controllers (and not minimal API) you'd have to be sure to call
AddMvc
/AddControllers
beforeAddProblemDetails
if you want it to use the code inDefaultApiProblemDetailsWriter
.
Exactly.
FWIW the outputs from the two are mostly the same anyways, though by default
DefaultApiProblemDetailsWriter
will addtraceId
extension (via the factory) while theDefaultProblemDetailsWriter
won't. A properly constructed customization callback could rectify that (if that's what one wants/expects).
The biggest difference between them is the MVC implementation supports content-negotiation, so, we can produce XML Problem Details.
Hi, I think this may be related, but the casing for ProblemDetails is also inconsistent. ProblemDetails itself defaults to camel case, but as you can see above when describing the "Summary" field, it's capitalized. Shouldn't that be lower case, assuming @TanvirArjel is using the default naming policy?
I've also noticed ProblemDetails does not obey JsonOptions
settings, e.g., setting
services.Configure<JsonOptions>(opt =>
{
opt.JsonSerializerOptions.PropertyNamingPolicy = null; // don't convert to camel case
});
should give a response like
{
"Type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
"Title": "Not Found",
"Status": 404,
"Detail": "Account 1 not found.",
"TraceId": "00-50f63c5d7921484e4797d82687f43033-915b95474bf55ff2-00"
}
but it does not, at least from what I can tell. Maybe this is by design since the spec (https://www.rfc-editor.org/rfc/rfc7807) explicitly lists the properties lower/regular camel case, but it also doesn't seem to explicitly call out camelcase must be used. I think anyone who is using problem details should be able to use whatever casing their API uses already rather than being forced to either a) have inconsistent casing returned or b) use camel case.
Hi, I think this may be related, but the casing for ProblemDetails is also inconsistent. ProblemDetails itself defaults to camel case, but as you can see above when describing the "Summary" field, it's capitalized. Shouldn't that be lower case, assuming @TanvirArjel is using the default naming policy?
In MVC for scenarios where the Validation Problem Details is auto generated, by default, it does not change it unless (.NET 7 only) you:
SystemTextJsonValidationMetadataProvider
(https://github.com/dotnet/aspnetcore/issues/39010) (recommended)services..AddControllers(options => options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider()))
JsonSerializerOptions.DictionaryKeyPolicy
(https://github.com/dotnet/aspnetcore/pull/38853)services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.DictionaryKeyPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
});
We have plans to make the provider default for ApiControllers
, but it will only come for .NET 8 (https://github.com/dotnet/aspnetcore/issues/40408)
I've also noticed ProblemDetails does not obey
JsonOptions
settings, e.g., setting
@jchoca Interesting, let me investigate but problem details serialization should use the naming policy. Can you share a simplified repro?
@jchoca I just took a look at our ProblemDetailsConverter
and I think you might be right since I don't see any usage of the options when we write, other than for Extensions
. Unless it is used internally in the Utf8JsonWriter
(that I need to investigate)
@brunolins16 thanks for checking! I created a simple web app here that illustrates the problem:
https://github.com/jchoca/ProblemDetailsJsonSettings
if you hit the root endpoint it returns pascal case, but if you hit /error
it returns camel case.
Any writer registered before the call to the
AddProblemDetails
will be evaluated before theDefaultProblemDetailsWrapper
I had to register my custom IProblemDetailsWriter
before the call to AddRazorPages
AND the call to AddProblemDetails
for it to be evaluated before the DefaultProblemDetailsWriter
.
Any writer registered before the call to the
AddProblemDetails
will be evaluated before theDefaultProblemDetailsWrapper
I had to register my custom
IProblemDetailsWriter
before the call toAddRazorPages
AND the call toAddProblemDetails
for it to be evaluated before theDefaultProblemDetailsWriter
.
@zwoolli If I am not mistaken AddRazorPages
probably calls AddMVCCore
(need to double check) that register a default writer for API Controllers. I believe your case the problem is not the DefaultProblemDetailsWriter
but the DefaultApiProblemDetailsWriter
. In both cases you are right and AddProblemDetails
must be called before.
@Rick-Anderson maybe we could add something about it in the docs if not there yet.
@Rick-Anderson maybe we could add something about it in the docs if not there yet.
See #29152
@zwoolli If I am not mistaken
AddRazorPages
probably callsAddMVCCore
(need to double check) that register a default writer for API Controllers. I believe your case the problem is not theDefaultProblemDetailsWriter
but theDefaultApiProblemDetailsWriter
. In both cases you are right andAddProblemDetails
must be called before.
@brunolins16 - You are correct. I looked into it and AddRazorPages
does indeed call AddMVCCore
,
AddMvcCoreServices
, DefaultApiProbtailsWriter
@TanvirArjel is this still an issue for you. If it is could you post a repro of the original issue including an example of the HTTP requests that you are firing into the controller? I tried to repro the issue you were describing and couldn't which makes me think that there is some context missing.
Hi @TanvirArjel. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.
See our Issue Management Policies for more information.
Is there an existing issue for this?
Describe the bug
The release note says that:
I have done so. Now I have the following endpoint:
Now if I don't provide the value of the summary then the response shows as follows:
This is fine!
But when I provide less than 100 chars, the response shows as follows:
Which is totally inconsistent. I want the response should be:
I have also tried the following but the result is the same:
.NET Version
.NET 7.0 Preview 7