Reputation: 2497
I'm writing a Web API service and trying to return a (400) Bad Request if my ModelState is invalid. I do not want a response body to be attached to this. It appears that IIS is hijacking my response and always returning a text/html content type with a lengthy, styled error page. This is a problem.
[HttpPost]
public void Link(LinkDeviceModel model)
{
if (ModelState.IsValid)
{
try
{
model.Save();
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
throw new HttpResponseException(ex.Message, HttpStatusCode.InternalServerError);
}
}
else
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
}
Here is my fiddler request:
POST http://localhost/myapp/service/link HTTP/1.1
Host: localhost
Content-Length: 112
Content-Type: application/json
Accept: application/json
{"DeviceUniqueId":"CC9C6FC0-7D06-11E1-8B0E-31564824019B", "UserName": "[email protected]"," Pin": "111111"}
And my response erroneous, full of body, response:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IIS 7.5 Detailed Error - 400.0 - Bad Request</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;background:#CBE1EF;}
code{margin:0;color:#006600;font-size:1.1em;font-weight:bold;}
.config_source code{font-size:.8em;color:#000000;}
pre{margin:0;font-size:1.4em;word-wrap:break-word;}
ul,ol{margin:10px 0 10px 40px;}
ul.first,ol.first{margin-top:5px;}
fieldset{padding:0 15px 10px 15px;}
.summary-container fieldset{padding-bottom:5px;margin-top:4px;}
legend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;}
legend{color:#333333;padding:4px 15px 4px 10px;margin:4px 0 8px -12px;_margin-top:0px;
border-top:1px solid #EDEDED;border-left:1px solid #EDEDED;border-right:1px solid #969696;
border-bottom:1px solid #969696;background:#E7ECF0;font-weight:bold;font-size:1em;}
a:link,a:visited{color:#007EFF;font-weight:bold;}
a:hover{text-decoration:none;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.4em;margin:10px 0 0 0;color:#CC0000;}
h4{font-size:1.2em;margin:10px 0 5px 0;
}#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS",Verdana,sans-serif;
color:#FFF;background-color:#5C87B2;
}#content{margin:0 0 0 2%;position:relative;}
.summary-container,.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
.config_source{background:#fff5c4;}
.content-container p{margin:0 0 10px 0;
}#details-left{width:35%;float:left;margin-right:2%;
}#details-right{width:63%;float:left;overflow:hidden;
}#server_version{width:96%;_height:1px;min-height:1px;margin:0 0 5px 0;padding:11px 2% 8px 2%;color:#FFFFFF;
background-color:#5A7FA5;border-bottom:1px solid #C1CFDD;border-top:1px solid #4A6C8E;font-weight:normal;
font-size:1em;color:#FFF;text-align:right;
}#server_version p{margin:5px 0;}
table{margin:4px 0 4px 0;width:100%;border:none;}
td,th{vertical-align:top;padding:3px 0;text-align:left;font-weight:bold;border:none;}
th{width:30%;text-align:right;padding-right:2%;font-weight:normal;}
thead th{background-color:#ebebeb;width:25%;
}#details-right th{width:20%;}
table tr.alt td,table tr.alt th{background-color:#ebebeb;}
.highlight-code{color:#CC0000;font-weight:bold;font-style:italic;}
.clear{clear:both;}
.preferred{padding:0 5px 2px 5px;font-weight:normal;background:#006633;color:#FFF;font-size:.8em;}
-->
</style>
</head>
<body>
<div id="header"><h1>Server Error in Application "DEFAULT WEB SITE/MYAPP"</h1></div>
<div id="server_version"><p>Internet Information Services 7.5</p></div>
<div id="content">
<div class="content-container">
<fieldset><legend>Error Summary</legend>
<h2>HTTP Error 400.0 - Bad Request</h2>
<h3>Bad Request</h3>
</fieldset>
</div>
<div class="content-container">
<fieldset><legend>Detailed Error Information</legend>
<div id="details-left">
<table border="0" cellpadding="0" cellspacing="0">
<tr class="alt"><th>Module</th><td>ManagedPipelineHandler</td></tr>
<tr><th>Notification</th><td>ExecuteRequestHandler</td></tr>
<tr class="alt"><th>Handler</th><td>System.Web.Http.WebHost.HttpControllerHandler</td></tr>
<tr><th>Error Code</th><td>0x00000000</td></tr>
</table>
</div>
<div id="details-right">
<table border="0" cellpadding="0" cellspacing="0">
<tr class="alt"><th>Requested URL</th><td>http://localhost:80/myapp/service/link</td></tr>
<tr><th>Physical Path</th><td>C:\workspace\myapp\service\link</td></tr>
<tr class="alt"><th>Logon Method</th><td>Anonymous</td></tr>
<tr><th>Logon User</th><td>Anonymous</td></tr>
</table>
<div class="clear"></div>
</div>
</fieldset>
</div>
<div class="content-container">
<fieldset><legend>Most likely causes:</legend>
<ul> <li></li> </ul>
</fieldset>
</div>
<div class="content-container">
<fieldset><legend>Things you can try:</legend>
<ul> <li>Create a tracing rule to track failed requests for this HTTP status code. For more information about creating a tracing rule for failed requests, click <a href="http://go.microsoft.com/fwlink/?LinkID=66439">here</a>. </li> </ul>
</fieldset>
</div>
<div class="content-container">
<fieldset><legend>Links and More Information</legend>
The request could not be understood by the server due to malformed syntax.
<p><a href="http://go.microsoft.com/fwlink/?LinkID=62293&IIS70Error=400,0,0x00000000,7601">View more information »</a></p>
<p>Microsoft Knowledge Base Articles:</p>
<ul><li></li></ul>
</fieldset>
</div>
</div>
</body>
</html>
I have tried to set TrySkipIisCustomErrors = True
with great hope, but no luck. Any ideas? Appreciated. Thanks.
Upvotes: 22
Views: 42379
Reputation: 1660
I had a situation where I was calling a WebApi endpoint from another, and having read that HttpClients should be static I made that change. Worked fine for a few dozen messages but then would get intercepted by the self-host instance which would return a 400 Bad Request. There didn't seem to be anything wrong, especially with the initial success, but my original code would add a default header to the client prior to the call. After I made the client static this would have meant that I was adding the header multiple times to the same client. I changed the code to send create and send an HttpRequestMessage instead of letting the client do it, and added the header to the request. Seems to have solved my problem.
Upvotes: 0
Reputation: 1120
I had the same problem with Asp.Net Webform application. The client sends ajax call to the server web handler (*.ashx).
I wanted to get the exception message back to the user. The Web handler sends back just the exception message and http status code 400.
The Trick is in Response.TrySkipIisCustomErrors = true;
try
{
//do some coding
}
catch (Exception exception)
{
Log.Error(exception);
context.Server.ClearError();
context.Response.TrySkipIisCustomErrors = true;
context.Response.ContentType = "text/plain";
context.Response.Write(exception.Message);
context.Response.StatusCode = 400;
//400 Bad Request
//The server cannot or will not process the request due to an apparent client error
//
//intentionally hidden exception
//prevent to send yellow page of death in ajax response
}
Upvotes: 3
Reputation: 1239
I was hitting this same condition, returning:
httpResponseMessage = context.Request.CreateResponse(statusCode);
Adding <httpErrors existingResponse="PassThrough"> didn't fix it, Content-type and the content body were missing in the response.
What did fix it was to construct the HttpResponseMessage like this:
var httpResponseMessage = context.Request.CreateResponse(statusCode, reasonPhrase);
By including the 'value' parameter (in this case, 'reasonPhrase') the Content-type and Content Body were present in the response.
Upvotes: 1
Reputation: 592
Assuming you're inside of your APIController and don't need an ExceptionFilterAttribute to do any additional work for the response, then just return a response with the error status code instead of throwing an HttpResponseException.
return Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid model.");
Upvotes: 3
Reputation: 21996
Try adding this to your web.config. I had a very similar problem which this solved.
<configuration>
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
Upvotes: 35
Reputation: 24125
Usually in such cases it is enough to set TrySkipIisCustomErrors = true on response object, but in case of Web API it sometimes doesn't do the trick (Web API even tries to set this flag internally by itself). For situations like this you can consider changing the IIS configuration. Please take a look here (you should be mostly interested in existingResponse="PassThrough").
Upvotes: 4