If I derive all of my exceptions from WebFaultException, and throw them directly from my services, the StatusCode set in the WebFaultException gets passed to the client. I'd rather not have to do that, since I'd rather throw generic exceptions like NotImplementedException, from my code.
I've set up an IErrorHandler, with the expectation that I'd be able to catch exceptions like NotImplementedException, and then just do the conversion to a WebFaultException there.
My code looks like this:
public class HTTPBindingErrorHandler: IErrorHandler
{
public void ProvideFault(Exception exception, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{
if (exception is NotImplementedException)
{
WebFaultException wfe = new WebFaultException(HttpStatusCode.NotImplemented);
MessageFault mf = wfe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, wfe.Action);
}
}
public bool HandleError(Exception exception)
{
return false;
}
}
When stepping through, I see that I'm getting to the code in ProvideFault, but the Status returned to the client is 400, not 501. Additionally, the body is:
<Fault
xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
<Code>
<Value>Receiver</Value>
<Subcode>
<Value
xmlns:a="http://schemas.microsoft.com/2009/WebFault">a:NotImplemented
</Value>
</Subcode>
</Code>
<Reason>
<Text xml:lang="en-US">Not Implemented</Text>
</Reason>
</Fault>
Why isn't the correct status code being returned?
Why isn't the body
in json?
Addition:
I've tried the recommendation of setting AutomaticFormatSelectionEnabled = false to the webhttpbehavior, but it still does not work. I'm self hosting, but I don't think that should make the difference. Here is the code I use to set up the service:
Uri newUri = new Uri(info.baseURI, info.Path);
WebServiceHost host = new WebServiceHost(info.Type, newUri);
WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.InheritedFromHost;
ServiceEndpoint ep = host.AddServiceEndpoint(info.Interface, binding, newUri);
ep.Behaviors.Add(new WebHttpBehavior() {
DefaultOutgoingResponseFormat = WebMessageFormat.Json,
DefaultBodyStyle = WebMessageBodyStyle.Wrapped,
AutomaticFormatSelectionEnabled = false } );
ep.Behaviors.Add(new WebHttpCors.CorsSupportBehavior());
ep.Behaviors.Add(new HTTPBindingErrorBehavior());
Related
I've overridden all model binding messages with translations using ModelBindingMessageProvider.SetValueIsInvalidAccessor and other ModelBindingMessageProvider values to return my custom resource strings.
And then I discovered the sad truth. If my API controller receives the data as JSON, then ModelBindingMessageProvider validation messages are not being used. Instead, Json.Net kicks in and I get something like this in response:
"errors": {
"countryId": [
"Input string '111a' is not a valid number. Path 'countryId', line 3, position 23."
]
},
I looked in GitHub source of Json.Net - indeed, it seems to have such exact error messages defined with line numbers etc.
So, ModelState manages to pull them in instead of using its own ModelBindingMessageProvider messages.
I tried to disable Json.Net error handling:
.AddJsonOptions(options =>
{
...
options.SerializerSettings.Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
// ignore them all
args.ErrorContext.Handled = true;
};
})
but it made no difference.
Is there any way to catch these Json deserialization errors and redirect them to ModelBindingMessageProvider, so that my localized messages would work?
Is there any way to catch these Json deserialization errors and
redirect them to ModelBindingMessageProvider, so that my localized
messages would work?
No, model binding and json input are different, model binder is for FromForm, and JsonInputFormatter is for FromBody. They are following different way. You could not custom the error message from ModelBindingMessageProvider.
For JSON, you may implement your own JsonInputFormatter and change the error message like
CustomJsonInputFormatter
public class CustomJsonInputFormatter : JsonInputFormatter
{
public CustomJsonInputFormatter(ILogger<CustomJsonInputFormatter> logger
, JsonSerializerSettings serializerSettings
, ArrayPool<char> charPool
, ObjectPoolProvider objectPoolProvider
, MvcOptions options
, MvcJsonOptions jsonOptions)
: base(logger, serializerSettings, charPool, objectPoolProvider, options, jsonOptions)
{
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var result = await base.ReadRequestBodyAsync(context);
foreach (var key in context.ModelState.Keys)
{
for (int i = 0; i < context.ModelState[key].Errors.Count; i++)
{
var error = context.ModelState[key].Errors[i];
context.ModelState[key].Errors.Add($"This is translated error { error.ErrorMessage }");
context.ModelState[key].Errors.Remove(error);
}
}
return result;
}
}
Register CustomJsonInputFormatter
services.AddMvc(options =>
{
var serviceProvider = services.BuildServiceProvider();
var customJsonInputFormatter = new CustomJsonInputFormatter(
serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<CustomJsonInputFormatter>(),
serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings,
serviceProvider.GetRequiredService<ArrayPool<char>>(),
serviceProvider.GetRequiredService<ObjectPoolProvider>(),
options,
serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value
);
options.InputFormatters.Insert(0, customJsonInputFormatter);
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
Register localized Service into CustomJsonInputFormatter to custom the error message.
I am trying to return a list from MVC controller. Value is returned easily when I am returning a hard code value. But when I am returning a list, it gives me an error. Here is my code ,
Ajax call,
function MyFunction() {
alert($('#DDlSurvey').val());
$.ajax({
url: "#Url.Action("Index", "ConductSurvey")",
data: { prefix: $('#DDlSurvey').val() },
type: "POST",
dataType: "json",
success: function (data) {
// loadData(data);
alert("Success");
// alert(data)
},
error: function (data){
alert("Failed! Please try again.");
}
});
//$('#YourLabelId').val('ReplaceWithThisValue');
}
and my method is,
[HttpPost]
public JsonResult Index(int prefix)
{
List<SelectList> Questions = new List<SelectList>();
List<Question> QuestionList = new List<Question>();
List<string> ll = new List<string>();
Question nn = new Question();
SurveyAppEntities ObjectSur = new SurveyAppEntities();
QuestionList = (from q in ObjectSur.Questions
join b in ObjectSur.SurveyQuestions on q.ID equals b.QuestionID
where b.SurveyID.Equals(prefix)
select q).ToList();
//return Json("OK");
return new JsonResult {Data=QuestionList, JsonRequestBehavior=JsonRequestBehavior.AllowGet};
}
When I return OK i get it but when i try to return QuestionList It is not returning data and display Failed
I am also using one link for help in which every thing is as it is link is,
http://www.dotnetawesome.com/2014/05/how-to-retrieve-database-data-show-using-jquery-mvc-asp.html
Hopes for your suggestion
A circular reference was detected indicates that EF generates proxy objects when generating query results which cannot be serialized using JsonResult. Assumed that SurveyAppEntities is your DbContext instance, then you need to do one of 2 options below:
1) Disable proxy creation
You need to set ProxyCreationEnabled property to false which prevent EF creating proxy objects which cannot be serialized:
[HttpPost]
public JsonResult Index(int prefix)
{
List<SelectList> Questions = new List<SelectList>();
List<Question> QuestionList = new List<Question>();
List<string> ll = new List<string>();
Question nn = new Question();
SurveyAppEntities ObjectSur = new SurveyAppEntities();
// this line is mandatory
ObjectSur.Configuration.ProxyCreationEnabled = false;
QuestionList = (from q in ObjectSur.Questions
join b in ObjectSur.SurveyQuestions on q.ID equals b.QuestionID
where b.SurveyID.Equals(prefix)
select q).ToList();
return Json(QuestionList, JsonRequestBehavior.AllowGet);
}
2) Disable query result tracking
EF implements query result tracking with proxy objects which may be disabled by adding AsNoTracking() to each table entities:
[HttpPost]
public JsonResult Index(int prefix)
{
List<SelectList> Questions = new List<SelectList>();
List<Question> QuestionList = new List<Question>();
List<string> ll = new List<string>();
Question nn = new Question();
SurveyAppEntities ObjectSur = new SurveyAppEntities();
QuestionList = (from q in ObjectSur.Questions
join b in ObjectSur.SurveyQuestions on q.ID equals b.QuestionID
where b.SurveyID.Equals(prefix)
select q).AsNoTracking().ToList(); // prevents result tracking
return Json(QuestionList, JsonRequestBehavior.AllowGet);
}
Note:
You may also try to return only required properties instead of entire ObjectSur.Questions object.
Related issue:
Circular reference detected exception while serializing object to JSON
Simply return list like below
return Json(QuestionList, JsonRequestBehavior.AllowGet);
I'm using EWS API for consuming outlook 365 mail service.
When I'm performing any mail operation it's running slow.
I have written the code mentioned below:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials("usernm", "pwd");
service.EnableScpLookup = false;
service.AutodiscoverUrl("user",RedirectionUrlValidationCallback);
That last line takes 16 seconds before the connection is successful.
Is there any way to make the performance faster?
Hard to say...not saying this is the answer
but try this and let me know.
I see a difference in that i dont pass RedirectionUrlValidationCallback in the autoDiscoverUrl and I don't set EnableScpLookup flag, dont know what that is for. let me know
public ExchangeService GetService( string autoDiscoverEmailAddress, string authEmailAddress,string authEmailPassword, string domain = null, ExchangeVersion verion = ExchangeVersion.Exchange2010_SP2 )
{
try
{
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
ExchangeService svc = new ExchangeService(verion);
//svc.UseDefaultCredentials = true;
if (!string.IsNullOrWhiteSpace(domain))
{
svc.Credentials = new WebCredentials(authEmailAddress, authEmailPassword, domain);
}
else
{
svc.Credentials = new WebCredentials(authEmailAddress, authEmailPassword);
}
svc.AutodiscoverUrl(autoDiscoverEmailAddress);
return svc;
}
catch (Exception)
{
throw;
}
}
The Nancy documentation seems to say that Pipelines.OnError should return null - as opposed to BeforeResponse which allows both null and a Response object.
All the examples like this one and many code samples here on StackOverflow show a Response being returned in the OnError, just like in the BeforeRequest.
When I attempt to return an HTTPStatus string for the Pipelines.OnError, everything works OK!
But when I attempt to return a Response, I get a compiler error:
Operator '+=' cannot be applied to operands of type 'Nancy.ErrorPipeline' and 'lambda expression'
I'm emulating almost exactly the code in the Nancy example, except for the fact that mine is a TinyIocContainer while the example's is using a StructureMap container and a StructureMap derived bootstrapper
Here's my code:
const string errKey = "My proj error";
const string creationProblem = "Message creation (HTTP-POST)";
const string retrievalProblem = "Message retrieval (HTTP-GET)";
public void Initialize(IPipelines pipelines)
{
string jsonContentType = "application/json";
byte[] jsonFailedCreate = toJsonByteArray(creationProblem);
byte[] jsonFailedRetrieve = toJsonByteArray(retrievalProblem);
Response responseFailedCreate = new Response
{
StatusCode = HttpStatusCode.NotModified,
ContentType = jsonContentType,
Contents = (stream) =>
stream.Write(jsonFailedCreate, 0, jsonFailedCreate.Length)
};
Response responseFailedRetrieve = new Response
{
StatusCode = HttpStatusCode.NotFound,
ContentType = jsonContentType,
Contents = (stream) =>
stream.Write(jsonFailedRetrieve, 0, jsonFailedRetrieve.Length)
};
// POST - error in Create call
pipelines.OnError += (context, exception) =>
{
// POST - error during Create call
if (context.Request.Method == "POST")
return responsefailedCreate;
// GET - error during Retrieve call
else if (context.Request.Method == "GET")
return responseFailedRetrieve;
// All other cases - not supported
else
return HttpStatusCode.InternalServerError;
};
}
private byte[] toJsonByteArray(string plainString)
{
string jsonString = new JObject { { errKey, plainString } }.ToString();
byte[] result = Encoding.UTF8.GetBytes(jsonString);
return result;
}
I had the same problem and I found a nice approach to the problem: http://paulstovell.com/blog/consistent-error-handling-with-nancy.
you should override RequestStartup on the Bootstrapper, here my test code:
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
pipelines.OnError.AddItemToEndOfPipeline((ctx, ex) =>
{
DefaultJsonSerializer serializer = new DefaultJsonSerializer();
Response error = new JsonResponse(ex.Message,serializer);
error.StatusCode = HttpStatusCode.InternalServerError;
return error;
});
base.RequestStartup(container, pipelines, context);
}
I have create a one sample WCF rest template WebApi in this i have use Entity Framework to getting the data when i run the service for it return the string value it showing the result but at end of the json value add XML code like below how can i solve this.
[{"AccountId":1,
"AccountNumber":"AC001",
"AccountType":"Restaurant",
"BusinessName":"Red SpiceInc",
"PrimaryContactFirstName":"Varma",
"PrimaryContactLastName":"Bhupatiraju",
"PrimaryContactPhone":"(949) 374 2114",
"PrimaryContactEmail":"redspice#mybusinessapp.com",
"AccountGuid":"918D3E66-CEFE-11E0-8C2F-0C0B4824019B",
"EntityState":1,"EntityKey":null}]
<?xml version="1.0" encoding="utf-8"?><Stream p1:nil="true" xmlns:p1="w3.org/2001/XMLSchema-instance"; />
My code
[WebGet(UriTemplate = "GetSetting({LocationGuid},{settingName})", ResponseFormat = WebMessageFormat.Json)]
public Stream GetSetting(string LocationGuid, string settingName)
{
string str = string.Empty;
string strJSON = string.Empty;
dynamic contactResponse = new JsonObject();
List<setting> Result;
Result = new List<setting>();
var Location = from acc in objEntity.locations where acc.LocationGuid == LocationGuid select acc;
if (Location.Count() > 0)
{
var LocationId = (from acc in objEntity.locations where acc.LocationGuid == LocationGuid select acc).First();
var objSetting = from cat in objEntity.settings where cat.SettingName == settingName & cat.LocationId == LocationId.LocationId select cat;
setting SettingList = new setting();
foreach (setting setting in objSetting)
{
setting Settinglist = new setting();
Settinglist.SettingId = setting.SettingId;
Settinglist.AccountId = setting.AccountId;
Settinglist.LocationId = setting.LocationId;
Settinglist.SettingName = setting.SettingName;
Settinglist.SettingValue = setting.SettingValue;
Settinglist.FieldType = setting.FieldType;
Result.Add(Settinglist);
}
JavaScriptSerializer js = new JavaScriptSerializer();
strJSON = js.Serialize(Result);
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
return new MemoryStream(Encoding.UTF8.GetBytes(strJSON));
}
else
{
return null;
}
}
Please help me solve this problem.
I believe return the POCO is enough
the method signature is
public List<setting> GetSetting(string, string)
WCF Web API will serialize the object into json or xml for you as per your request header (accept: application/json, or application/xml)
As was mentioned in misaxi's response, the WebApi WebGet operations generally do not need to concern themselves with how the response is returned. The responsibility of the web operation is to simply return the data. E.g....
[WebGet(UriTemplate = "GetSetting({LocationGuid},{settingName})"]
public List<setting> GetSetting(string LocationGuid, string settingName)
{
List<setting> Result = null;
var Location = from acc in objEntity.locations where acc.LocationGuid == LocationGuid select acc;
if (Location.Count() > 0)
{
Result = new List<setting>();
....
foreach (setting setting in objSetting)
{
....
Result.Add(Settinglist);
}
}
return Result;
}
The fact that the client receives XML, JSON, JSONP, HTML, etc is up to the client (in conjunction with server-side support). The client's request header will include something that looks like Accept: application/json or Accept: application/xml or whatever representation that the client is after. WebApi comes pre-loaded with a few standard formats, XML (the default) and Json. You can introduce more formats as needed to accept a more diverse set of Accept: .... headers, but the conversion from your data to these requested formats are generally out-of-scope for your web operation and rely on the formatters you set up (or were set up by default) when your service is initialized.
Likewise, WebPost operations generally do not care if the body of the request was XML or JSON, by the time the operation is invoked, formatters have already translated that payload into your method's parameters.