c# - How to return data in a standard way? -
i trying figure out how return data in standard way. mean when return json or xml nice have 1 format everything(success , errors).
say have following json result.
{ "person": { "id": 12345, "firstname": "john", "lastname": "doe", "phones": { "home": "800-123-4567", "work": "888-555-0000", "cell": "877-123-1234" }, "email": [ "jd@example.com", "jd@example.org" ], "dateofbirth": "1980-01-02t00:00:00.000z", "registered": true, "emergencycontacts": [ { "name": "", "phone": "", "email": "", "relationship": "spouse|parent|child|other" } ] } }
this fine happens if there validation error
i use built in method createerrorresponse
{ "message": "the request invalid.", "modelstate": { "item": [ "required property 'name' not found in json. path '', line 1, position 14." ], "item.name": [ "the name field required." ], "item.price": [ "the field price must between 0 , 999." ] } }
*yes know data not make sense , different data irrelevant structure is.
now happens if have error , in case has custom error code.
i return this(using httperror)
{ "message": "my custom error message", "customerrorcode": 37 }
now can see have 3 different formats of json coming back. on client have this
- check httpstatuscode
- if 200 in case parse json using format of person.
- if 400 validation error or server error.
- if customer error found use format otherwise use modlestate.
i been working foursquare , seems return same format user have no clue how same sort of thing when it.
{ "meta": { "code": 200, ...errortype , errordetail... }, "notifications": { ...notifications... }, "response": { ...results... } }
i similar like
would ok request.
{ "meta": { "code": 200, "modelstate": {} }, "response": { "person": { "id": 12345, "firstname": "john", "lastname": "doe", "phones": { "home": "800-123-4567", "work": "888-555-0000", "cell": "877-123-1234" }, "email": [ "jd@example.com", "jd@example.org" ], "dateofbirth": "1980-01-02t00:00:00.000z", "registered": true, "emergencycontacts": [ { "name": "", "phone": "", "email": "", "relationship": "spouse|parent|child|other" } ] } } }
server error this
{ "meta": { "code": 500, "message": "this server error", "modelstate": {} }, "response": {} }
validation this
{ "meta": { "code": 400, "message": "validation errors", "message": "the request invalid.", "modelstate": { "item": [ "required property 'name' not found in json. path '', line 1, position 14." ], "item.name": [ "the name field required." ], "item.price": [ "the field price must between 0 , 999." ] } }, "response": {} }
but said not sure how , not 100% best way still. @ least should 1 format then?
edit @erik philips
when doing asp.net mvc projects this.
public readonly ivalidation validation; public personservice(ivalidation validation) { this.validation = validation; } public person getperson(int id) { try { return findperson(id); } catch(exception ex) { //log real error elmah validation.adderror("internal", "something went wrong"); } } public class personcontroller { public readonly ipersonservice personservice; public personcontroller(ipersonservice personservice) { this.personservice = personservice; } public actionresult getperson(int id) { personservice.getperson(id); if(personservice.validation.isvalid) { // } else { // else } return view(); } }
i how set keep sort of way. don't think can use interface thinking of this
public personservice() { } public responseresult<person> getperson(int id) { var result = responseresult<person>(); try { return findperson(id); } catch(exception ex) { result.errorcode = 200; result.msg = "failed"; } } public class personcontroller { public readonly ipersonservice personservice; public personcontroller(ipersonservice personservice) { this.personservice = personservice; } public httpresponsemessage getperson(int id) { var result = personservice.getperson(id); if(result.isvalid) { request.createresponse<responseresult<person>>(httpstatuscode.ok, result); } request.createresponse<responseresult<person>>(httpstatuscode.badrequest, result); } }
this sort of large question it's design send data has multiple parts, believe easy, small , elegant solution.
this isn't use, example:
first lets build out model represents responses need, or can used when no result data required:
public class responseresult { public responseresult() { } public responseresult(modelstatedictionary modelstate) { this.modelstate = new modelstateresult (modelstate); } // request valid, in context of actual request public bool isvalid { get; set; } // serialized model state if needed public modelstateresult modelstate { get; set; } }
next, there large set of different types of results return, , here generics come in handy:
public class responseresult<t> : responseresult { public responseresult() : base() { } public responseresult(modelstatedictionary modelstate) : base(modelstate) { } public responseresult(t data, modelstatedictionary modelstate) : base (modelstate) { this.data = data; } public t data { get; set; } }
so if need return person
can return:
var result = responseresult<person>(); result.data = person; //serialize result , send client.
my apis can consumed javascript change http status code, , give examples on how use jquery redirect , consume data.
request = $.ajax({ type: "post", url: url, data: data, success: function(data, textstatus, jqxhr) { processresponseresult(data); } complete: function(e, xhr, settings) { if(e.status === 401) { // login } // else if (e.status == ) else { // unknown status code } )};
you may want extend result consumed client may not using http (wcf) in future:
public class responseresult { .... .... public int errorcode { get; set; } public string errormessage { get; set; } }
or take step further:
public class responseerrorbase { public int errorcode { get; set; } public string errormessage { get; set; } } public class responseresult { .... .... public responseerrorbase error { get; set; } }
so add more error types/information in future.
update per comments
comment 1: if have collection of people have..
list<person> persons = new list<person>(); var result = new responseresult<list<person>>(); result.data = persons;
comment 2: there 2 classes..
if api had call fileexists(filename)
don't have return object, call succeeded.
var result = new responseresult(); result.isvalid = fileexists(filename);
if api wanted return id of new person
return new id.
var result = new responseresult<guid?>(); result.isvalid = createperson(personinfo); if (result.isvalid) { result.data = personinfo.id; }
or return new successful person
object, or null if not successful.
var result = new responseresult<person>(); result.isvalid = createperson(personinfo); if (result.isvalid) { result.data = person; }
update per comments
what recommend wrote earlier , include responseerrorbase
in responseresult:
public class responseresult { public responseresult() { } public responseresult(modelstatedictionary modelstate) { this.modelstate = new modelstateresult (modelstate); } public bool isvalid { get; set; } public modelstateresult modelstate { get; set; } public responseerrorbase error { get; set; } }
then derive error base specific:
// isn't abstract because may want return // non-specific error messages public class responseerrorbase { public int code { get; set; } public string message { get; set; } } public class internalresponseerror : responseerrorbase { // property specific error // not errors public int internalerrorlogid { get; set; } }
then return (example returning value, you'll want more logic):
var result = new responseresult<person>(); try { result.data = db.findperson(id); } catch (sqlexception ex) { var error = responseerrorbase(); error.code = 415; error.message = "sql exception"; } catch (exception ex) { var error = internalresponseerror(); error.internalerrorlogid = log.writeexception(ex); error.code = 500; error.message = "internal error"; } // mvc might like: return this.json(result);
Comments
Post a Comment