2

HeyGuys

I'm working on a WebApi project that receives requests from clients and redirects these requests to other services that are not open for direct access.

By default, .Net serializes and deserializes the Json request parameters automatically, so I need to re-serialize them before calling the appropriate service. The same problem occurs when receiving the service response. I need to deserialize it before sending the response to the user; otherwise .Net framework will serialize it one more time, resulting in a "Json of Json" response.

I found this answer but it does not seem to work with .NetCore; so I tried to create my own ModelBinder that just reads the Json object and returns it.

class JsonUnformatterBinderProvider : Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinderProvider
  {
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {      
      return new JsonUnformatterBinder(new SimpleTypeModelBinder(context.Metadata.ModelType));      
    }
  }

and

class JsonUnformatterBinder : IModelBinder
{
    private readonly IModelBinder _fallbackBinder;

    public JsonUnformatterBinder(IModelBinder fallbackBinder)
    {
      _fallbackBinder = fallbackBinder;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
      string currMethod = bindingContext.ActionContext.HttpContext.Request.Method;

      if ("POST".Equals(currMethod) || "PUT".Equals(currMethod))
      {
        string strData = new StreamReader(bindingContext.ActionContext.HttpContext.Request.Body).ReadToEnd();
        bindingContext.Result = ModelBindingResult.Success(strData);
        return Task.CompletedTask;
      }

      return _fallbackBinder.BindModelAsync(bindingContext);
    }
}

This code is very simple, it was my first attempt and it worked well for my purposes. However, I still get the "Json of Json" problem when I take the second service answer and returns back to the user.

I basically have no idea what I can do to overcome this, so any workaround is welcome here.

Renato Medeiros
  • 443
  • 5
  • 13

1 Answers1

0

If you need just redirect a request without modification, you could read it from input stream directly and send it to inner service. You could also use such approach to read responce from inner service.

//1. Set empty parameter list in action then neither serializator nor model binder are not invoked.
public async Task<ContentResult> ProxyAction(/*empty parameter list*/)
{
    var newUrl = @"https://stackoverflow.com";
    var data = this.Request.Body;

    using (var client = new HttpClient())
    {
        //2. Read request body from input stream.
        var reader = new StreamReader(data);
        var json = reader.ReadToEnd();

        using (var content = new StringContent(json))
        {
            //3. Set correct content type
            content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(this.Request.ContentType);

            //4. Post request to inner service
            var response = await client.PostAsync(newUrl, content);

            //5. Read response without deserialization
            var innerResponse = await response.Content.ReadAsStringAsync();
            var contentType = response.Content.Headers.ContentType.ToString();
            var statusCode = response.StatusCode;

            //6. Return inner response without serialization
            var outerResponse = this.Content(innerResponse, contentType);
            outerResponse.StatusCode = (int)statusCode;
            return outerResponse;
        }
    }
}
Ivan R.
  • 1,875
  • 1
  • 13
  • 11