We have a WebApi in AspNet core 2.2 that uses AspNetCore.OData version 7.2.1.
For security reasons we want to have full control over the responses when exceptions are thrown, so OData stacktraces are not send back to our Api consumers.
However, some OData exceptions cannot be caught in the API; They are swallowed by the OData layer.
We are looking for a way to catch these exceptions, but are unable to do so.
Below is a way to reproduce these kind of exceptions.
Microsoft.AspNetCore.OData 7.2.1
Reproduction steps:
public class Book
{
public long Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public bool Available { get; set; }
}
````
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
//Error logging goes here
await context.Response.WriteAsync("An error has occurred. Check the log.");
});
});
public static void Validate(ODataQueryOptions queryOptions)
{
try
{
queryOptions.Validate(new ODataValidationSettings() { });
}
catch (Exception e)
{
//Will be a specific exception in production code
throw new Exception(e.Message, e)
{ };
}
}
}
````
Exception is caught by the application's exception handler
OR
Exception is thrown by the ODataQueryOptions.Validate() (and in our case redirected to the application's exception handler)
No exception is caught by the application. OData swallows/catches the exception and returnes a 400 response with full stacktrace:
{
"Message": "The query specified in the URI is not valid. The 'startswith' function cannot be applied to an enumeration-typed argument.",
"ExceptionMessage": "The 'startswith' function cannot be applied to an enumeration-typed argument.",
"ExceptionType": "Microsoft.OData.ODataException",
"StackTrace": " at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.ValidateAllStringArguments(String functionName, Expression[] arguments)\r\n at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.BindStartsWith(SingleValueFunctionCallNode node)\r\n at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.BindSingleValueFunctionCallNode(SingleValueFunctionCallNode node)\r\n at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.BindSingleValueNode(SingleValueNode node)\r\n at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.Bind(QueryNode node)\r\n at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.BindExpression(SingleValueNode expression, RangeVariable rangeVariable, Type elementType)\r\n at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.BindFilterClause(FilterBinder binder, FilterClause filterClause, Type filterType)\r\n at Microsoft.AspNet.OData.Query.Expressions.FilterBinder.Bind(IQueryable baseQuery, FilterClause filterClause, Type filterType, ODataQueryContext context, ODataQuerySettings querySettings)\r\n at Microsoft.AspNet.OData.Query.FilterQueryOption.ApplyTo(IQueryable query, ODataQuerySettings querySettings)\r\n at Microsoft.AspNet.OData.Query.ODataQueryOptions.ApplyTo(IQueryable query, ODataQuerySettings querySettings)\r\n at Microsoft.AspNet.OData.EnableQueryAttribute.ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)\r\n at Microsoft.AspNet.OData.EnableQueryAttribute.ExecuteQuery(Object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, Func`2 modelFunction, IWebApiRequestMessage request, Func`2 createQueryOptionFunction)\r\n at Microsoft.AspNet.OData.EnableQueryAttribute.OnActionExecuted(Object responseValue, IQueryable singleResultCollection, IWebApiActionDescriptor actionDescriptor, IWebApiRequestMessage request, Func`2 modelFunction, Func`2 createQueryOptionFunction, Action`1 createResponseAction, Action`3 createErrorAction)"
}
Seeing the same problem here in .NET Core 3.1 and OData 7.3.0
Facing the same problem in .NET Core 3.1 and OData 7.3.0
Seeing the same problem here in .NET Core 3.1 and OData 7.3.0
Me too!!
I'm facing the same issue!
Does anyone know a way to handle this exception?
@KanishManuja-MS Is there a way to handle this?
@KanishManuja-MS Is there a way to handle this?
I'm facing the same issue!
Does anyone know a way to handle this exception?
One way it worked for me is to explicitly call 'queryOption.Validate()'. this will throw an exception if there is any issue. The same can be caught my middleware.
My solution:
public class ErrorHandlingMiddleware {
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */ ) {
bool modifyResponse = true;
Stream originBody = null;
if (modifyResponse) {
//uncomment this line only if you need to read context.Request.Body stream
//context.Request.EnableRewind();
originBody = ReplaceBody(context.Response);
}
try {
await next(context);
} catch (Exception ex) {
await HandleExceptionAsync(context, ex);
} finally {
//as we replaced the Response.Body with a MemoryStream instance before,
//here we can read/write Response.Body
//containing the data written by middlewares down the pipeline
//finally, write modified data to originBody and set it back as Response.Body value
ReturnBody(context.Response, originBody);
}
}
private Stream ReplaceBody(HttpResponse response) {
var originBody = response.Body;
response.Body = new MemoryStream();
return originBody;
}
private void ReturnBody(HttpResponse response, Stream originBody) {
response.Body.Seek(0, SeekOrigin.Begin);
response.Body.CopyTo(originBody);
response.Body = originBody;
}
private Task HandleExceptionAsync(HttpContext context, Exception ex) {
var code = HttpStatusCode.InternalServerError; // 500 if unexpected
var result = JsonConvert.SerializeObject(new {Error= ex.Message});
context.Response.Body.Seek(0, SeekOrigin.Begin);
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int) code;
return context.Response.WriteAsync(result);
}
}
In Startup.cs
//Add in Configure before UseMvc
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
Usually errors that occur outside the MVC pipeline has to be handled by a middleware. These errors include:
If you are returning an un-evaluated IQueryable (deferred) then you might have SQL execution errors, but also if there's some Serialization issue (circular dependencies, etc.)...
One way it worked for me is to explicitly call 'queryOption.Validate()'. this will throw an exception if there is any issue. The same can be caught my middleware.
Thanks for the revert.
When I pass some query as expand, my api gives correct result and in case of in vague query parameter for example "$expand=qwe123" it gives me 400 bad request with exception message : "Query specified in URI is invalid"
type : ODataException
Is there any way that I can handle this in my controller?
in the controller get method.... use OdataQueryOptions explicitly... like the one below.. or u can catch that error in middleware.
Supporting ODataQueryOptions in existing Web API
|
|
|
| | |
|
|
|
| |
Supporting ODataQueryOptions in existing Web API
I have a Web API project which has been used for several years without OData support, just with standard URL par...
|
|
|
On Thursday, 18 June, 2020, 06:44:52 pm IST, Ishan Shah <[email protected]> wrote:
One way it worked for me is to explicitly call 'queryOption.Validate()'. this will throw an exception if there is any issue. The same can be caught my middleware.
Thanks for the revert.
When I pass some query as expand, my api gives correct result and in case of in vague query parameter for example "$expand=qwe123" it gives me 400 bad request with exception message : "Query specified in URI is invalid"
type : ODataException
Is there any way that I can handle this in my controller?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or unsubscribe.
Using the custom EnableQueryAttribute in the original comment by @mabouwhui fixes the problem in our application in that it redirects the ODataException to our own error handling logic, but I want to add my +1 to the original issue because the current error handling exposes the full stack trace and implementation detail in production. This seems like a big security leak the OData team seems willing to handle in most other cases. Severity on this one should be escalated, IMHO.
Using the custom
EnableQueryAttributein the original comment by @mabouwhui fixes the problem in our application in that it redirects the ODataException to our own error handling logic, but I want to add my +1 to the original issue because the current error handling exposes the full stack trace and implementation detail in production. This seems like a big security leak the OData team seems willing to handle in most other cases. Severity on this one should be escalated, IMHO.
I have created the CustomEnableQueryAttribute and used it for endpoints. Debugger strikes try block , but exception is not caught. Debugger does not enters the catch block.

This is with oData-7.5
Most helpful comment
My solution:
In Startup.cs