ServiceStack doesn't populate the response DTO when throwing HttpErrors - error-handling

ServiceStack doesn't populate the original response in the WebServiceException's responseDTO property.
I'm running the code below which should always return a 404 response code with the ResponseStatus property of the TestResponse populated with "Some bad request" but it also seems like should return the original good response with it's output property populated from the request's input property. However I get null when I look at the WebServiceException responseDTO
public TestResponse Post(Test request)
{
var response = new TestResponse() { Output = request.Input };
throw new HttpError(response, (int)HttpStatusCode.BadRequest, "Some bad request");
}
public TestResponse Get(Test request)
{
try
{
using (var client = new JsonServiceClient("http://localhost:5000"))
{
var response = client.Post(request);
return response;
}
}
catch (WebServiceException ex)
{
throw;
}
}
In general I was expecting that the responseDTO property in the WebServiceException will contain the endpoint's DTO as long as it's passed in when throwing the HttpError but that doesn't seem to be the case. I see only default values and nulls for each property in the responseDTO.

When an Exception is thrown only the ResponseStatus is preserved, you can add any additional info to its Meta dictionary.
Alternatively you can return a failed HTTP Response:
public TestResponse Post(Test request)
{
var response = new TestResponse() { Output = request.Input };
base.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return response;
}

Related

Net Core 3.1 OData returns error 500 when using both $filter and $select and output is xml

I'm currently developing an OData API in .Net Core 3.1 which fetches data from SQL server. Using postman, I'm sending GET requests to the API with Accept headers text/xml and application/json.
With this url: <http://localhost:8008/odata/Contact?$filter=No_ eq 'T20-1234567'&$select=No_> and an application/json Accept-Header (or No Accept-Header) the response is
json response
But with Accept-Header application/xml or text/xml:
An unhandled exception was thrown by the application.
System.ArgumentException: Object of type 'System.Linq.EnumerableQuery1[Microsoft.AspNet.OData.Query.Expressions.SelectExpandBinder+SelectSome1[Models.Contact]]' cannot be converted to type 'System.Collections.Generic.IEnumerable`1[Models.Contact]'.
The strange thing is that when the $select part is removed from the url, the request is correctly handled by the application (200).
My controller action:
[HttpGet]
[ODataRoute(nameof(Contact))]
public IQueryable<Contact> GetContact()
{
return _context.Contact;
}
Has anyone seen this type of behaviour?
To answer my own question, here's a possible workaround:
services
.AddMvc(config =>
{
...
config.OutputFormatters.Add(new CustomXmlOutputFormatter());
config.RespectBrowserAcceptHeader = true;
});
public class CustomXmlOutputFormatter : TextOutputFormatter
{
public CustomXmlOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanWriteType(Type type)
{
return true;
}
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (selectedEncoding == null) throw new ArgumentNullException(nameof(selectedEncoding));
var httpContext = context.HttpContext;
var json = JsonConvert.SerializeObject(new { item = context.Object });
var xml = JsonConvert.DeserializeXNode(json, "root");
var buffer = new StringBuilder(xml.ToString());
await httpContext.Response.WriteAsync(buffer.ToString());
}
}

How do I get the model state from a HTTPRequestResponse from within a console application

I have an APS.NET Core 2.0 API that I am writing a test client for. The test client is a console application. I want to be able to read and display any errors returned from may API call that would be in the model state.
In my API, if the model is not valid, I return the model along with a status 422 as follows;
if (!ModelState.IsValid)
{
return new UnprocessableEntityObjectResult(ModelState);
}
The UnprocessableEntityObjectResult is just a helper class, as shown below;
public class UnprocessableEntityObjectResult : ObjectResult
{
public UnprocessableEntityObjectResult(ModelStateDictionary modelState)
: base(new SerializableError(modelState))
{
if (modelState == null)
{
throw new ArgumentNullException(nameof(modelState));
}
StatusCode = 422;
}
}
My intent is to return the modelstate to the client, on error.
My test client is a console application and I am looking for a way to examine the model state and list out any errors.
In my console application, I have the following method that is called from Main;
static async Task CreateUploadRecordAsync()
{
httpClient.BaseAddress = new Uri("https://localhost:44369");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string relativeUrl = "/api/upload";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, relativeUrl);
HttpResponseMessage response = new HttpResponseMessage();
using (var content = new MultipartFormDataContent())
{
//Add content values here...
request.Content = content;
response = await httpClient.SendAsync(request);
}
if (response.IsSuccessStatusCode)
{
string result = response.Headers.Location.ToString();
Console.WriteLine("Success:\n");
Console.WriteLine($"New Record Link: [{result}]\n");
}
else
{
//Add code here to get model state from response
Console.WriteLine($"Failed to create new upload record. Error: {response.ReasonPhrase}\n");
}
}
I am looking for an example of how to extract the model state that would exist after the "/Add code here to get model state from response" comment.
Any ideas?

Dropwizard / JerseyClient ignored JsonProperty when sending http request

I have two REST services implemented with Dropwizard-0.8.
Both share an API dependency with following POJO:
public class Report{
private String text;
#JsonProperty("t")
public String getText()
{
return text;
}
public void setText(String tx)
{
text = tx;
}
}
My Server has a rest recourse:
#POST
#Consumes(MediaType.APPLICATION_JSON + ";charset=UTF-8")
#Produces(MediaType.TEXT_PLAIN + ";charset=UTF-8")
#Timed
public Response receive(Report dto) {
//do some stuff with dto
}
My Client has a method :
sendReport(report);
with:
private void sendReport(Report report) {
final String uri = "http://localhost:8080/.....";
Response response = null;
try {
response = client.target(uri).request().post(Entity.entity(report, MediaType.APPLICATION_JSON), Response.class);
final int status = response.getStatus();
if (status != Status.ACCEPTED.getStatusCode()) {
final StatusType statusInfo = response.getStatusInfo();
throw new SomeException();
}
}
catch (Exception e) {
LOGGER.error(e.getMessage());
}
finally {
if (response != null) {
response.close();
}
}
}
The Client is made in the Dropwizard application class with:
service.client = new JerseyClientBuilder(env).using(conf.getJerseyClient()).withProvider(JacksonJaxbJsonProvider.class).build(getName());
env.jersey().register(service);
Where 'service' is my rest class calling the 'sendReport' method.
Problem
When I call the rest service of my server from a browser or with curl etc it works perfectly as expected with following messagebody:
{"t":"some text for the server"}
But when I run my application to call the rest service I get a 400 "unable to process JSON".
Debugging and the log messages showed me that the application sends the following JSON to my server:
{"text":"some text for the server"}
Which leads to the error that Jackson cant find a property "text".
Why is the JerseyClient ignoring the JsonProperty annotation?
From what I understand you using Entity.entity from jersey which has no idea about the #JsonProperty annotation(which is from jackson library) . What you need to do is do serialisation using a jackson library and give it to post call .

Restlet: Get response body on failed get()

I am trying to debug a particular connection that responds with a Unauthorized (401). In Restlet this causes Respresentation.get() to throw an error.
What I want to do is get the response body as this particular api gives you greater error information in the body.
Any suggestions?
Thanks,
Luke
I would be reluctant to reccomend it for production but certainly for debugging you can override handleInbound in ClientResource to change the conditions under which an error is thrown. Restlet will then return the body, in the usual way.
ClientResource clientResource = new ClientResource("http://your.url") {
#Override
public Representation handleInbound(Response response) {
Representation result = null;
if (response.getRequest().isSynchronous()) {
if (response.getStatus().isError()
&& !Status.CLIENT_ERROR_UNAUTHORIZED.equals(response.getStatus())) {
doError(response.getStatus());
} else {
result = (response == null) ? null : response.getEntity();
}
}
return result;
}
};
Representation response = clientResource.get();
System.out.println(response.getText());

Returning 'Allow' Entity Header with HTTPResponseMessage in WCF Web API

I'm trying to return the 'Allow' entity header within the response message and I keep getting the following message:
{"The header cannot be added. Make sure to add request headers to HttpRequestMessage, response headers to HttpResponseMessage, and content headers to HttpContent objects."}
Here's the code snippet:
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
public HttpResponseMessage<Order> DeleteOrder(int id)
{
HttpResponseMessage<Order> response = null;
try
{
if (id <= 0)
{
response = new HttpResponseMessage<Order>(HttpStatusCode.BadRequest);
}
else
{
// For brevity, I'm assuming that order - 123456 was already served and logged. Hence it cannot
// be deleted. Order 12345, however, can be deleted.
// Note: The code doesn't actual delete anything. This is just a demonstration of
// the DELETE verb
if (id == 12345)
{
return new HttpResponseMessage<Order>(HttpStatusCode.NoContent);
}
if (id == 123456)
{
response = new HttpResponseMessage<Order>(HttpStatusCode.MethodNotAllowed);
response.Headers.AddWithoutValidation("Allow", "PUT");
}
// return '404 - Not Found' status code
response = new HttpResponseMessage<Order>(HttpStatusCode.NotFound);
}
return response;
}
catch (Exception ex)
{
return response = new HttpResponseMessage<Order>(HttpStatusCode.InternalServerError);
}
}
Any advice would be very helpful.
Thanks,
dorman
Try response.Content.Headers instead.