Search code examples
elasticsearchasp.net-web-apinestelasticsearch-5

JsonResult Response from WebAPI for Elasticsearch NEST query


I have created WEBAPI methods, to search ES using NEST. The search with ES is working as expected. The issue that I have is when I try to return the Json(response) using POSTMAN, it throws this exception:

{ "Message": "An error has occurred.", "ExceptionMessage": "If you use a custom contract resolver be sure to subclass from ElasticContractResolver", "ExceptionType": "System.Exception", "StackTrace": " at Nest.JsonExtensions.GetConnectionSettings(JsonSerializer serializer)\r\n at Nest.VerbatimDictionaryKeysJsonConverter2.WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeConvertable(JsonWriter writer, JsonConverter converter, Object value, JsonContract contract, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n at System.Web.Http.Results.JsonResult1.Serialize()\r\n at System.Web.Http.Results.JsonResult1.Execute()\r\n at System.Web.Http.Results.JsonResult1.ExecuteAsync(CancellationToken cancellationToken)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.AuthenticationFilterResult.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()" }

This is my WEB API method

[System.Web.Http.Route("SearchElastic")]
[System.Web.Http.HttpPost]

public JsonResult<ISearchResponse<T>> SearchElastic([FromBody] ElasticSearchRequest esRequest)
{
    var searchResponse = EsClient.Search<T>(
        "...NEST query..."
                ));

    return Json(searchResponse);
}

//<T> is a custom C# class.

I am using Elasticsearch/NEST 5.x.


Solution

  • The error message hints at what the problem is

    { "Message": "An error has occurred.", "ExceptionMessage": "If you use a custom contract resolver be sure to subclass from ElasticContractResolver", "ExceptionType": "System.Exception", "StackTrace": " at ...

    and it relates to the line

        return Json(searchResponse);
    

    the configuration for the Json serializer used by Web API does not know how to handle serializing certain types from NEST because some of those types need ElasticContractResolver to be serialized correctly.

    There are a few ways to solve this, but the two most straightforward are:

    1. Configure the Json Serializer used by Web API with ElasticContractResolver as the IContractResolver on the JsonSerializerSettings. The disadvantage here is that all of your types that you're serializing to json will be going through NEST's resolver

    2. Avoid the deserialization/re-serialization loop from Elasticsearch -> deserialized by NEST -> reserialized by Web API altogether by using the low level client to return a string or byte array from Elasticsearch. I would recommend this approach if you are not doing any introspection on searchResponse. If you do need to inspect searchResponse, you can always deserialize the string or byte array to SearchResponse<T> as a separate step.

    This is one of the reasons that the direct dependency on Json.NET has been removed in NEST 6.x