简体   繁体   中英

ASP.NET MVC Action returns 500 on invalid JSON

I am creating an API for a partner company to upload a report, and it looks something like this:

[Route("UploadReport"), HttpPost]
public async Task<ActionResult> UploadReport(CancellationToken cancellationToken)
{
    if (Request.ContentType?.ToLower()?.StartsWith("application/json") != true)
    {
        return new MyJsonResult(HttpStatusCode.UnsupportedMediaType, "{\"Error\":\"Content-Type must be \\\"application\\/json\\\"\"}");
    }

    var json = new { reportContentType = "", reportContent = "" };
    Request.InputStream.Seek(0, SeekOrigin.Begin);

    try
    {
        json = JsonConvert.DeserializeAnonymousType(await new StreamReader(Request.InputStream, GetRequestEncoding(Request)).ReadToEndAsync(), json);
    }
    catch
    {
        return new MyJsonResult(HttpStatusCode.BadRequest, "{\"Error\":\"Failed to parse the JSON.\"}");
    }

    // From here, I go on to store the result and return an OK response.
}

The point is that I want to require them to send a Content-Type: application/json Header, and I want to return a 400 Bad Request if I fail to parse the JSON.

But if I send a POST with invalid JSON (note the missing comma after "text/html" ):

POST https://[debug-server]/UploadReport HTTP/1.1
Host: [debug-server]
Content-Length: 95
Content-Type: application/json

{
  "reportContentType":"text/html"
  "reportContent":"<h2>This is an example report</h2>"
}

I get this 500 error:

500错误

(So you can search this):

Server Error in '/' Application.

...

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.ArgumentException: Invalid object passed in, ':' or '}' expected. (41): {
  "reportContentType":"text/html"
  "reportContent":"<h2>This is an example report</h2>"
}

Source Error: 


An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  

Stack Trace: 



[ArgumentException: Invalid object passed in, ':' or '}' expected. (41): {
  "reportContentType":"text/html"
  "reportContent":"<h2>This is an example report</h2>"
}]
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeDictionary(Int32 depth) +1251
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth) +123
System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer) +79
System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit) +37
System.Web.Mvc.JsonValueProviderFactory.GetDeserializedObject(ControllerContext controllerContext) +213
System.Web.Mvc.JsonValueProviderFactory.GetValueProvider(ControllerContext controllerContext) +16
System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext) +69
System.Web.Mvc.ControllerBase.get_ValueProvider() +30
System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +62
System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +105
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__19(AsyncCallback asyncCallback, Object asyncState) +743
System.Web.Mvc.Async.WrappedAsyncResult`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +343
System.Web.Mvc.Controller.<BeginExecuteCore>b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +25
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +465
System.Web.Mvc.Controller.<BeginExecute>b__14(AsyncCallback asyncCallback, Object callbackState, Controller controller) +18
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +20
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +374
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +16
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +52
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30
System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +384
System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +103
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155



--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1087.0

If I don't include the Content-Type: application/json Header and remove the clause to require it, it works fine. So there's something that sees a request with Content-Type: application/json and tries to parse it, which throws an exception on invalid JSON.

So the two questions I have are:

What is causing this?

How do I prevent it?

Look for Request Validation. Turn it off. It must help.

So the key here was to drop all parameters in the signature. So instead of taking a parameter of type CancellationToken , I can use Request.TimedOutToken instead. So now my signature looks like this:

[Route("UploadReport"), HttpPost]
public async Task<ActionResult> UploadReport()

Without any parameters in the method, MVC's request handler quit trying to parse the JSON to provide a value for the parameter(s), and I can parse the JSON from the Request myself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM