Good Afternoon all,
I am writing a program that calls and API, maps the json response to a corresponding data model. then will ultimately write those maps to disk.
The following code snippet is the main entry point for the program.
public static async Task Main(string[] args)
{
try
{
await WriteFile(2010);
}
catch (Exception ex)
{
Console.WriteLine($"There was an exception: {ex.ToString()}");
}
}
public static async Task WriteFile(int year)
{
ApiHelper.InitializeClient();
var data = await DataLoader.LoadData(year);
StreamWriter writer = new StreamWriter(#"Data.txt");
writer.WriteLine(data.Id);
writer.Close();
}
The DataLoader is as follows
public static async Task<BigDataModel> LoadData(int year)
{
using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(GetUri(year)))
{
if (response.IsSuccessStatusCode)
{
DataModel data = await response.Content.ReadAsAsync<DataModel>();
return data.Meta;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
Within the if statement the program returns an exception regarding my json format being incorrect and needing to be deserialized. I have tested this system without attempting to map and array and it works correctly.
My Data Models are as followed
public class DataModel
{
public BigDataModel Meta { get; set; }
}
public class BigDataModel
{
public int Id { get; set; }
}
expected Json response
{
"projected":"value",
"actual":"value",
"meta":["data":"1","id":"data"]
}
What are the best practices when mapping a json object(array) to an object.
Is deserialization a must or can I create a work around to write the data to disk.
Related
I cannot find any resource, that would say I cannot do that.
I have all setup hub/client and tested when parameter is decimal, but once I use generic class, then the server wont react.
SignalRMessage:
public class SignalRMessage<T>
{
public SignalRMessage(T value, string text)
{
Value = value;
Text = text ?? string.Empty;
}
public T Value { get; set; }
public string Text { get; set; }
}
Hub (OnConnected gets a hit):
public class JobHeaderHub : Hub
{
public override Task OnConnectedAsync()
{
Debug.WriteLine(Clients.Caller);
return base.OnConnectedAsync();
}
public async Task JobHeaderUpdated(SignalRMessage<decimal> message)
{
await Clients.Others.SendAsync("ReceiveJobHeaderUpdated", message);
}
public async Task JobHeaderCreated(SignalRMessage<decimal> message)
{
await Clients.Others.SendAsync("ReceiveJobHeaderCreated", message);
}
}
Client:
public class JobHeaderSingalRClient
{
private HubConnection connection;
public JobHeaderSingalRClient()
{
// connection = new HubConnectionBuilder().WithUrl(#"").WithAutomaticReconnect().Build();
connection = new HubConnectionBuilder().WithUrl(#"http://localhost:5000/jobheader").WithAutomaticReconnect().Build();
connection.On<SignalRMessage<decimal>>("ReceiveJobHeaderUpdated", message => JobHeaderUpdated?.Invoke(message));
connection.On<SignalRMessage<decimal>>("ReceiveJobHeaderCreated", message => JobHeaderCreated?.Invoke(message));
}
public static async Task<JobHeaderSingalRClient> CreateConnectedClient()
{
var client = new JobHeaderSingalRClient();
await client.ConnectAsync();
return client;
}
public async Task<JobHeaderSingalRClient> ConnectAsync()
{
await connection.StartAsync();
return this;
}
public event Action<SignalRMessage<decimal>> JobHeaderUpdated;
public async Task SendJobHeaderUpdated(decimal id, string message = null)
{
await connection.SendAsync("JobHeaderUpdated", new SignalRMessage<decimal>(id, message));
}
public event Action<SignalRMessage<decimal>> JobHeaderCreated;
public async Task SendJobHeaderCreated(decimal id, string message = null)
{
await connection.SendAsync("JobHeaderCreated", new SignalRMessage<decimal>(id, message));
}
}
I have no idea why when parameter is SignalRMessage<decimal> then the methods on server are not getting hit. Anyone knows? Thanks.
I had this sort of issues too when I was using constructors with parameters. All of them disappeared after adding a default parameterless constructor.
This is most probably not related to signalR, but to the underlying JSON serialization.
The type has to be specified in order to be able to serialize the objects.
I had similar issues when using objects of type object as parameters.
To troubleshoot turn on verbose error messages in signalR and see if there are any errors logged.
services.AddSignalR(options =>
{
options.Hubs.EnableDetailedErrors = true;
});
I am using Entity Framework. Below is an example of a list method for an Actors context in my ActorsDao class. If you imagine my application is like imdb, there will be CRUD methods for various other contexts such as Movies, Directors, Genres, Reviews, Studios etc.
Regardless of the method or context, I handle errors in the same way. Due to my many methods across many contexts, my catch section is always exactly the same.
Obviously, I could create an error handling class, put the code in there, and just call a method in that class from the catch block.
However, I'm wondering if there a way to omit the TRY...CATCH from each method and set up a global error handler for the methods in my entity framework layer?
I would only want this global error handler to handle these errors and not errors from the rest of the application.
I seem to remember in Java Spring, you could annotate a class or method with the name of a method, and all errors would be passed to that without the need of a TRY...CATCH. I'm wondering if there is something similar for .NET (or a third party library with such functionality)?
public List<Actor> ListActors()
{
List<Actor> actorList = new List<Actor>();
using (var context = new ActorContext())
{
try
{
actorList = context.Actors.ToList<Actor>();
}
catch (Exception e)
{
//Handle error code
}
}
return actorList;
}
EDIT
I did some more research and found this code from here https://stackoverflow.com/a/4851985/1753877
private void GlobalTryCatch(Action action)
{
try
{
action.Invoke();
}
catch (ExpectedException1 e)
{
throw MyCustomException("Something bad happened", e);
}
catch (ExpectedException2 e)
{
throw MyCustomException("Something really bad happened", e);
}
}
public void DoSomething()
{
GlobalTryCatch(() =>
{
// Method code goes here
});
}
Would using a delegate like this be OK? It certainly meets my requirements.
You can create a class like this and extend the controller from this class.
Error Handler class looks like this :
package com.wes.essex.rest;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import com.wes.essex.bean.ErrorResponse;
public class SkyNewsController {
private static final Logger LOGGER = LoggerFactory.getLogger(SkyNewsController.class);
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleError(Exception ex) {
LOGGER.info("start");
LOGGER.error(ex.getMessage(), ex);
ErrorResponse error = new ErrorResponse();
error.setTimestamp(ZonedDateTime.now().format(DateTimeFormatter.ISO_INSTANT));
LOGGER.debug("error : {} ", error);
ResponseEntity<ErrorResponse> response = null;
if (ex instanceof ConstraintViolationException) {
error.setReasonCode(HttpStatus.BAD_REQUEST.value());
ConstraintViolationException constraintException = (ConstraintViolationException) ex;
Set<ConstraintViolation<?>> set = constraintException.getConstraintViolations();
String errorMessage = "Input Validation Failed:";
for (ConstraintViolation<?> constraintViolation : set) {
errorMessage += constraintViolation.getMessageTemplate() + ",";
}
errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
error.setErrorMessage(errorMessage);
response = new ResponseEntity<ErrorResponse>(error, HttpStatus.BAD_REQUEST);
} else {
error.setReasonCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
error.setErrorMessage(ex.getMessage());
response = new ResponseEntity<ErrorResponse>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
return response;
}
}
This would be the baean class for error response :
package com.wes.essex.bean;
public class ErrorResponse {
private static final long serialVersionUID = 5776681206288518465L;
private String timestamp;
private String errorMessage;
private int reasonCode;
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public int getReasonCode() {
return reasonCode;
}
public void setReasonCode(int reasonCode) {
this.reasonCode = reasonCode;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
We are using the OAuthAuthorizationServerProvider class to do authorization in our ASP.NET Web Api app.
If the provided username and password is invalid in GrantResourceOwnerCredentials, the call
context.SetError( "invalid_grant", "The user name or password is incorrect." );
Produces the following Json result:
{
"error": "invalid_grant",
"error_description": "The user name or password is incorrect."
}
Is there any way to customize this error result?
I would like to make it consistent with default error message format used in other parts of the API:
{
"message": "Some error occurred."
}
Is this possible to achieve with the OAuthAuthorizationServerProvider?
This is how I did it.
string jsonString = "{\"message\": \"Some error occurred.\"}";
// This is just a work around to overcome an unknown internal bug.
// In future releases of Owin, you may remove this.
context.SetError(new string(' ',jsonString.Length-12));
context.Response.StatusCode = 400;
context.Response.Write(jsonString);
+1 for Dasun's answer. Here is how I extended it a bit further.
public class ErrorMessage
{
public ErrorMessage(string message)
{
Message = message;
}
public string Message { get; private set; }
}
public static class ContextHelper
{
public static void SetCustomError(this OAuthGrantResourceOwnerCredentialsContext context, string errorMessage)
{
var json = new ErrorMessage(errorMessage).ToJsonString();
context.SetError(json);
context.Response.Write(json);
}
}
The .ToJsonString() is another extension method that uses the Newtonsoft.Json library.
public static string ToJsonString(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
Usage:
context.SetCustomError("something went wrong");
1+ again for "user2325333" and "Dasun's" answer his solution, your answers are good but still there is an issue .
The Josn Tag still return {error:""}, thus I replace the context.Response.Body with empty MemoryStream
and here the work example
public static class ContextHelper
{
public static void SetCustomError(this OAuthGrantResourceOwnerCredentialsContext context,string error, string errorMessage)
{
var json = new ResponseMessage
{ Data = errorMessage, Message = error, IsError = true }.ToJsonString();
context.SetError(json);
context.Response.Write(json);
Invoke(context);
}
public static string ToJsonString(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
static async Task Invoke(OAuthGrantResourceOwnerCredentialsContext context)
{
var owinResponseStream = new MemoryStream();
var customResponseBody = new System.Net.Http.StringContent(JsonConvert.SerializeObject(new ResponseMessage()));
var customResponseStream = await customResponseBody.ReadAsStreamAsync();
await customResponseStream.CopyToAsync(owinResponseStream);
context.Response.ContentType = "application/json";
context.Response.ContentLength = customResponseStream.Length;
context.Response.Body = owinResponseStream;
}
}
public class ResponseMessage
{
public bool IsError { get; set; }
public string Data { get; set; }
public string Message { get; set; }
}
for usage of this context
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (!context.Match.Passcode)
{
context.SetCustomError("invalid_grant", "Passcode is invalid.");
return;
}
}
The Result will be as
So I have modified the todo service that gives me a response.
When I deliberately throw an unhandled error to test ServiceExceptionHandler code below, if I use the default /Backbone.Todos/todo/1?format=json, it is fine.
But if I use /Backbone.Todos/todo/1?format=xml, it says:
XML Parsing Error: not well-formed Location:
/Backbone.Todos/todo/1 Line Number 1, Column 2:
Here is my code:
public class AppHost : AppHostBase {
public override void Configure(Funq.Container container) {
//Unhandled errors
this.ServiceExceptionHandler = (req, ex) => {
var res = new { //i return a dynamic object here ... and it doesn't work
Result = null as object,
ResponseStatus = new ResponseStatus() {
ErrorCode = "Error",
Message = "Not available",
StackTrace = "Not available"
}
};
return res;
};
//...
}
//....
}
//My normal request/response dtos look like this:
//-----------------------------------------
[Route("/todo/{id}", "GET")] //display req
public class GetTodo : IReturn<TodoResponse> {
public long Id { get; set; }
}
public class TodoResponse : IHasResponseStatus {
public IList<Todo> Result { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
The thing is, when error occured I can't construct a correct type of response without knowing what type it is. I only get incoming Request and Exception as paramaters, but not Response type.
I suspect the error is because XML cannot automatically serialize dynamic objects in .net.
I not entirely sure I know what you are trying to do but you could try using the built in utilities to handle the exception then modify it as you want.
this.ServiceExceptionHandler = (req, ex) =>
{
var responseStatus = ex.ToResponseStatus();
var errorResponse = ServiceStack.ServiceHost.DtoUtils.CreateErrorResponse(req, ex, responseStatus);
return errorResponse;
};
I have a problem testing webservice that has its own de/serialization mechanism provided.
My sample Task class that is being used by TaskService:
public class Task
{
public string TaskName { get; set; }
public string AuxData { get; set; }
public static void RegisterCustomSerialization(IAppHost appHost)
{
appHost.ContentTypeFilters.Register("application/xml", SerializeTaskToStream, DeserializeTaskFromStream);
}
public static void SerializeTaskToStream(IRequestContext requestContext, object response, Stream stream)
{
var tasks = response as List<Task>;
if (tasks != null)
{
using (var sw = new StreamWriter(stream))
{
if (tasks.Count == 0)
{
sw.WriteLine("<Tasks/>");
return;
}
sw.WriteLine("<Tasks>");
foreach (Task task in tasks)
{
if (task != null)
{
sw.WriteLine(" <Task type=\"new serializer\">");
sw.Write(" <TaskName>");
sw.Write(task.TaskName);
sw.WriteLine("</TaskName>");
sw.Write(" <AuxData>");
sw.Write(task.AuxData);
sw.WriteLine("</AuxData>");
sw.WriteLine(" </Task>");
}
}
sw.WriteLine("</Tasks>");
}
}
else
{
var task = response as Task;
using (var sw = new StreamWriter(stream))
{
if (task != null)
{
sw.WriteLine(" <Task type=\"new serializer\">");
sw.Write(" <TaskName>");
sw.Write(task.TaskName);
sw.WriteLine("</TaskName>");
sw.Write(" <AuxData>");
sw.Write(task.AuxData);
sw.WriteLine("</AuxData>");
sw.WriteLine(" </Task>");
}
}
}
}
public static object DeserializeTaskFromStream(Type type, Stream stream)
{
if (stream == null || stream.Length == 0)
return null; // should throw exception?
XDocument xdoc = XDocument.Load(stream);
XElement auxData = xdoc.Root.Element("AuxData");
return new Task() { AuxData = auxData.Value };
}
public override bool Equals(object obj)
{
Task task = obj as Task;
if (task == null)
return false;
return TaskName.Equals(task.TaskName);
}
public override int GetHashCode()
{
return TaskName.GetHashCode();
}
}
I have based my serialization / deserialization code on: http://www.servicestack.net/ServiceStack.Northwind/vcard-format.htm and https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/ServiceStack.Northwind/ServiceStack.Northwind.ServiceInterface/VCardFormat.cs
My base test class is as follows:
public class SimpleRestTestBase : AppHostBase
{
public SimpleRestTestBase() : base( "SimpleRestTestBase", typeof(TaskService).Assembly)
{
Instance = null;
Init();
}
public override void Configure(Funq.Container container)
{
SetConfig(new EndpointHostConfig
{
DefaultContentType = ContentType.Xml
}
);
Task.RegisterCustomSerialization(this);
Routes
.Add<Task>("/tasks/{TaskName}")
.Add<List<Task>>("/tasks");
container.Register(new List<Task>());
}
}
And the unit test that fails:
[TestFixture]
public class SimpleTest : SimpleRestTestBase
{
[Test]
public void TestMetodRequiringServer()
{
var client = (IRestClient)new XmlServiceClient("http://localhost:53967");
var data = client.Get<List<Task>>("/api/tasks");
}
}
The exception I get when using nUnit test runner is:
Testing.SimpleTest.TestMetodRequiringServer:
System.Runtime.Serialization.SerializationException : Error in line 1 position 9. Expecting element 'ArrayOfTask' from namespace 'http://schemas.datacontract.org/2004/07/ServiceStackMVC'.. Encountered 'Element' with name 'Tasks', namespace ''.
How do I pass information about my custom serialization/deseialization code to the XmlServiceClient?
You're overriding the generic XML Serialization format (application/xml) with a custom version that is strongly-coupled to only handle 1 web service output - this is very rarely what you want since it will prevent (i.e. break) all your other services from returning XML.
If you want to return custom XML, just limit to the services that need it by returning a xml string instead.
You can't change the implementation of XmlServiceClient as it is strongly coupled to the XML Serialization/DeSerialization that ServiceStack uses. You should use a raw HTTP Client to send the exact XML payload you want. Here's an example sending raw XML with .NET's web request:
https://stackoverflow.com/a/8046734/85785
Since you're returning and sending custom XML you may also want to override the Custom Request Binder for your web service so you have an opportunity to deserialize the request how you want.
See the wiki page below for some examples on how to do this:
https://github.com/ServiceStack/ServiceStack/wiki/Serialization-deserialization
Note: returning custom XML is not ideal since it by-passes many of the advantages of ServiceStack's strong-typed, intelligent and opinionated nature.