Servicestack - Multiple IReturn on Request DTO - c#

Is it possible to have multiple IReturn<> on a request DTO?
For example following route:
[Route("/api/whatever", "GET,POST,PUT,DELETE")]
public class WhateverRequest : IReturn<bool>, IReturn<List<Whatever>>
{
public string WhateverId { get; set; }
}
Depending on the request method I want to have another IReturn.
Post-Put-Delete Request should only return a acknowledge if the request was successful:
IReturn<bool>
but on a GET request I want to have a:
IReturn<List<Whatever>>
It would also be good if there is a way to reflect this in Swagger Api/ Metadata Page.
Currently only the first IReturn is shown.
Is this possible or would it be better to create a route for each different IReturn?

You definitely want to be creating different routes to handle the multiple return types. Only one IReturn<T> or IReturnVoid is expected, or consuming clients wouldn't know how to type the returned data correctly.
[Route("/api/whatever", "GET")]
public class ListWhateverRequest : IReturn<List<Whatever>>
{
public string WhateverId { get; set; }
}
// Action
public List<Whatever> Get(ListWhateverRequest request)
{
...
}
[Route("/api/whatever", "POST,PUT,DELETE")]
public class UpdateWhateverRequest : IReturn<bool>
{
public string WhateverId { get; set; }
}
// Action
public bool Post(UpdateWhateverRequest request)
{
...
}
public bool Put(UpdateWhateverRequest request)
{
...
}
public bool Delete(UpdateWhateverRequest request)
{
...
}
I presume you are returning true from these methods to show they completed successfully. Do the methods ever return false when something goes wrong, or is an exception thrown instead? If you are only throwing exception in the method, and never returning false then instead of returning bool consider using void methods with IReturnVoid. The request is therefore successful if it doesn't throw an exception.

Related

.Net Core Access Response From Custom Middleware Before Serialization

I am working on a custom middlware that's going to set the response Http Status Code based on the response itself.
I have a class:
public class Response<T>
{
public T Data { get; set; }
public IEnumerable<CustomError> Errors { get; set; }
}
that is returned by every controller in my .Net Core API.
I want to create a custom middleware that's going to access the response after it is returned from the controller and it will assign a correct Http Status Code in the Response based on the Errors field.
I can see some solutions for accessing the Response.Body field of the HttpContext, but it would provide a serialized string that I would have to deserialize again and that's running around in circles.
Is it possible in .Net Core?
Best regards,
Marcin
Instead of middleware, you could create an ActionFilter, specifically your own implementation IAsyncResultFilter. It's going to be easier to cast to Response in the MVC context rather than in the middleware because you may access there ObjectResult.
It could look like this.
public class Response
{
public IEnumerable<string> Errors { get; set; }
}
public class Response<T> : Response
{
public T Data { get; set; }
}
Note that I changed the Response<T> class to make casting easier.
public class ErrorResultFilter : IAsyncResultFilter
{
public Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var result = context.Result as ObjectResult;
var response = result?.Value as Response;
if (response != null)
context.HttpContext.Response.StatusCode
= response.Errors.Any(x => x.Equals("SomeError")) ? 400 : 200;
return next();
}
}
This sample sets status code based on the presence of "SomeError". There's no serialization/deserialization involved, just casting.
services.AddControllers(o =>
{
o.Filters.Add(typeof(ErrorResultFilter));
});
This way, I registered my filter in the startup.cs

How to define a REST method in C# with abstract parameter

The API Call
I am making a REST API call with the following message body:
{"Method":{"Token":"0","Value":"0"}}
400 Response
I am getting a 400 Bad Request response from the api with the following body:
{"Message":"The request is invalid.","ModelState":{"request.Method.Token":["Could not create an instance of type Namespace.ActionMethod. Type is an interface or abstract class and cannot be instantiated. Path 'ActionMethod.Token'."]}}
Code Information
The method which is receiving the api call looks like this:
public MethodResponse MakeMethodCall([Required] [FromBody] MethodRequest request)
MethodRequest has a Method property which is an abstract type.
public class MethodRequest
{
public ActionMethod Method { get; set; }
}
public abstract class ActionMethod
{
public string Token { get; set; }
}
public class FirstMethod : ActionMethod
{
public string Value { get; set; }
}
Question
How can I call the REST API and have it recognize that the type of Method is FirstMethod, instead of it trying to instantiate the abstract type ActionMethod?
Note that I will need to have more implementations of ActionMethod in the future (ie. SecondMethod), so the solution will need to include an extensible ActionMethod (interface would also be fine).
EDIT
It would also be reasonable to include an enum to identify which implementation of ActionMethod was being targeted by the API call.
I'm currently using a solution which has an ActionMethodType enum and both FirstMethod and SecondMethod fields. I'm checking these fields based on the value of ActionMethodType. This works, but I would like to have a single [Required] field into which I could pass any implementation of ActionMethod.
Can't be done. How would the framework know to instantiate FirstMethod for this parameter? What if you had another subclass of ActionMethod that also had a Value property? Now it's even more ambiguous for the framework to figure out on it's own. You could do a bunch of work, creating a custom formatter (http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx) but ultimately it would be easier to just have a single class that includes all possible properties a client could send OR have separate API endpoints for the client to call using different concrete types as the parameter.
If I understand you correctly, you could implement this with a custom model binder and a factory pattern.
public class MethodRequestBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
HttpRequestBase request = controllerContext.HttpContext.Request;
//use the request object to make a call to your factory for the
//appropriate ActionMethod subtype you want to create, or however
//else you see fit.
var curActionMethod = MyFactory.Get(request.QueryString);
var boundObj = new MethodRequest()
{
Method = curActionMethod
}
return boundObj;
}
}
register your model binder in app_start:
ModelBinders.Binders.Add(typeof(MethodRequest), new MethodRequestBinder());
now, just decorate your controller action method:
public ActionResult Index([ModelBinder(typeof(MethodRequestBinder))] MethodRequest request)
{
//etc..
}
I used this as a starting point: http://www.codeproject.com/Articles/605595/ASP-NET-MVC-Custom-Model-Binder
Remove the abstract keyword from your ActionMethod, or mark the Token property abstract and override it in the inherited classes:
public abstract class ActionMethod
{
public abstract string Token { get; set; }
}
public class FirstMethod : ActionMethod
{
public string Value { get; set; }
public override string Token
{
get;
set;
}
}

Asp.net webapi enum parameter with default value

I have a controller
[HttpGet]
[RoutePrefix("api/products/{productId}")]
public HttpResponseMessage Products(int productId,TypeEnum ptype=TypeEnum.Clothes)
{
if(!Enum.IsDefined(typeOf(TypeEnum),ptype))
//throw bad request exception
else
//continue processing
}
Myenum is declared as
public TypeEnum
{
Clothes,
Toys,
Electronics
}
Currently if,some garbage value is passed it is getting converted into default value.
What I want to do is if i call the controller as api/products/1 then the ptype should be assigned default value i.e clothes. If I call the controller as api/products/1?pType=somegarbagevalue then the controller should throw bad request exception. How can I achieve this?
Defining all your enum parameters as strings and then parsing them everywhere means you have to do this on every single action and you will need to come up with a consistent approach such that all parsing errors conform.
This is a parameter binding issue and should not be dealt with in the controller layer, it should be taken care of in the pipeline. One way to do this is to create a custom filter and add it to your config.
public class ModelStateValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = <your standardised error response>
}
}
}
And in your global.asax.cs
...
GlobalConfiguration.Configure(WebApiConfig.Register);
...
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
config.Filters.Add(new ModelStateValidationAttribute());
...
}
}
If you're having trouble with the model state, it's type is a ModelStateDictionary and you simply iterate over it and then it's Errors property contains all the model binding issues. e.g.
modelState = actionContext.ModelState;
modelState.ForEach(x =>
{
var state = x.Value;
if (state.Errors.Any())
{
foreach (var error in state.Errors)
{
<work your magic>
}
}
});
You have to do with string and use TryParse() to convert string to Enum value.
public HttpResponseMessage Products(int productId,string ptype="Clothes")
{
TypeEnum category = TypeEnum.Clothes;
if(!Enum.TryParse(ptype, true, out category))
//throw bad request exception if you want. but it is fine to pass-through as default Cloathes value.
else
//continue processing
}
It may look naive but the benefit of this approach is to allow ptype parameter to whatever string and to perform process without exception when ptype fails to bind the value.
This type of validation should be handled in pipeline not in controller.
public abstract class ETagMatchAttribute : ParameterBindingAttribute
{
private ETagMatch _match;
public ETagMatchAttribute(ETagMatch match)
{
_match = match;
}
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
if (parameter.ParameterType == typeof(ETag))
{
return new ETagParameterBinding(parameter, _match);
}
return parameter.BindAsError("Wrong parameter type");
}
}
something like this. refer to MSDN link for detailed explanation

How do I return NotFound() IHttpActionResult with an error message or exception?

I am returning a NotFound IHttpActionResult, when something is not found in my WebApi GET action. Along with this response, I want to send a custom message and/or the exception message (if any). The current ApiController's NotFound() method does not provide an overload to pass a message.
Is there any way of doing this? or I will have to write my own custom IHttpActionResult?
Here's a one-liner for returning a IHttpActionResult NotFound with a simple message:
return Content(HttpStatusCode.NotFound, "Foo does not exist.");
You'd need to write your own action result if you want to customize the response message shape.
We wanted to provide the most common response message shapes out of the box for things like simple empty 404s, but we also wanted to keep these results as simple as possible; one of the main advantages of using action results is that it makes your action method much easier to unit test. The more properties we put on action results, the more things your unit test needs to consider to make sure the action method is doing what you'd expect.
I often want the ability to provide a custom message as well, so feel free to log a bug for us to consider supporting that action result in a future release:
https://aspnetwebstack.codeplex.com/workitem/list/advanced
One nice thing about action results, though, is that you can always write your own fairly easily if you want to do something slightly different. Here's how you might do it in your case (assuming you want the error message in text/plain; if you want JSON, you'd do something slightly different with the content):
public class NotFoundTextPlainActionResult : IHttpActionResult
{
public NotFoundTextPlainActionResult(string message, HttpRequestMessage request)
{
if (message == null)
{
throw new ArgumentNullException("message");
}
if (request == null)
{
throw new ArgumentNullException("request");
}
Message = message;
Request = request;
}
public string Message { get; private set; }
public HttpRequestMessage Request { get; private set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
public HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NotFound);
response.Content = new StringContent(Message); // Put the message in the response body (text/plain content).
response.RequestMessage = Request;
return response;
}
}
public static class ApiControllerExtensions
{
public static NotFoundTextPlainActionResult NotFound(this ApiController controller, string message)
{
return new NotFoundTextPlainActionResult(message, controller.Request);
}
}
Then, in your action method, you can just do something like this:
public class TestController : ApiController
{
public IHttpActionResult Get()
{
return this.NotFound("These are not the droids you're looking for.");
}
}
If you used a custom controller base class (instead of directly inheriting from ApiController), you could also eliminate the "this." part (which is unfortunately required when calling an extension method):
public class CustomApiController : ApiController
{
protected NotFoundTextPlainActionResult NotFound(string message)
{
return new NotFoundTextPlainActionResult(message, Request);
}
}
public class TestController : CustomApiController
{
public IHttpActionResult Get()
{
return NotFound("These are not the droids you're looking for.");
}
}
You could use ResponseMessageResult if you like:
var myCustomMessage = "your custom message which would be sent as a content-negotiated response";
return ResponseMessage(
Request.CreateResponse(
HttpStatusCode.NotFound,
myCustomMessage
)
);
yeah, if you need much shorter versions, then I guess you need to implement your custom action result.
You may use ReasonPhrase property of HttpResponseMessage class
catch (Exception exception)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
{
ReasonPhrase = exception.Message
});
}
You can create a custom negotiated content result as d3m3t3er suggested. However I would inherit from. Also, if you need it only for returning NotFound, you don't need to initialize the http status from constructor.
public class NotFoundNegotiatedContentResult<T> : NegotiatedContentResult<T>
{
public NotFoundNegotiatedContentResult(T content, ApiController controller)
: base(HttpStatusCode.NotFound, content, controller)
{
}
public override Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken)
{
return base.ExecuteAsync(cancellationToken).ContinueWith(
task => task.Result, cancellationToken);
}
}
one line code in asp.net core:
Return StatusCode(404, "Not a valid request.");
I solved it by simply deriving from OkNegotiatedContentResult and overriding the HTTP code in the resulting response message. This class allows you to return the content body with any HTTP response code.
public class CustomNegotiatedContentResult<T> : OkNegotiatedContentResult<T>
{
public HttpStatusCode HttpStatusCode;
public CustomNegotiatedContentResult(
HttpStatusCode httpStatusCode, T content, ApiController controller)
: base(content, controller)
{
HttpStatusCode = httpStatusCode;
}
public override Task<HttpResponseMessage> ExecuteAsync(
CancellationToken cancellationToken)
{
return base.ExecuteAsync(cancellationToken).ContinueWith(
task => {
// override OK HTTP status code with our own
task.Result.StatusCode = HttpStatusCode;
return task.Result;
},
cancellationToken);
}
}
I was needing to create an IHttpActionResult instance in the body of an IExceptionHandler class, in order to set the ExceptionHandlerContext.Result property. However I also wanted to set a custom ReasonPhrase.
I found that a ResponseMessageResult could wrap a HttpResponseMessage (which allows ReasonPhrase to be set easily).
For Example:
public class MyExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
var ex = context.Exception as IRecordNotFoundException;
if (ex != null)
{
context.Result = new ResponseMessageResult(new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = $"{ex.EntityName} not found" });
}
}
}
If you inherit from the base NegotitatedContentResult<T>, as mentioned, and you don't need to transform your content (e.g. you just want to return a string), then you don't need to override the ExecuteAsync method.
All you need to do is provide an appropriate type definition and a constructor that tells the base which HTTP Status Code to return. Everything else just works.
Here are examples for both NotFound and InternalServerError:
public class NotFoundNegotiatedContentResult : NegotiatedContentResult<string>
{
public NotFoundNegotiatedContentResult(string content, ApiController controller)
: base(HttpStatusCode.NotFound, content, controller) { }
}
public class InternalServerErrorNegotiatedContentResult : NegotiatedContentResult<string>
{
public InternalServerErrorNegotiatedContentResult(string content, ApiController controller)
: base(HttpStatusCode.InternalServerError, content, controller) { }
}
And then you can create corresponding extension methods for ApiController (or do it in a base class if you have one):
public static NotFoundNegotiatedContentResult NotFound(this ApiController controller, string message)
{
return new NotFoundNegotiatedContentResult(message, controller);
}
public static InternalServerErrorNegotiatedContentResult InternalServerError(this ApiController controller, string message)
{
return new InternalServerErrorNegotiatedContentResult(message, controller);
}
And then they work just like the built-in methods. You can either call the existing NotFound() or you can call your new custom NotFound(myErrorMessage).
And of course, you can get rid of the "hard-coded" string types in the custom type definitions and leave it generic if you want, but then you may have to worry about the ExecuteAsync stuff, depending on what your <T> actually is.
You can look over the source code for NegotiatedContentResult<T> to see all it does. There isn't much to it.
Iknow PO asked with a message text, but another option to just return a 404 is making the method return a IHttpActionResult and use the StatusCode function
public async Task<IHttpActionResult> Get([FromUri]string id)
{
var item = await _service.GetItem(id);
if(item == null)
{
StatusCode(HttpStatusCode.NotFound);
}
return Ok(item);
}
Answers here are missing a little developer story problem. The ApiController class is still exposing a NotFound() method that developers may use. This would cause some 404 response to contain a uncontrolled result body.
I present here a few parts of code "better ApiController NotFound method" that will provide a less error-prone method that does not require developers to know "the better way of sending a 404".
create a class inheriting from ApiController called ApiController
I use this technique to prevent developers from using the original class
override its NotFound method to let devs use the first available api
if you want to discourage this, mark this as [Obsolete("Use overload instead")]
add an extra protected NotFoundResult NotFound(string message) that you want to encourage
problem: the result does not support responding with a body. solution: inherit and use NegotiatedContentResult. see attached better NotFoundResult class.
Another nice possibility is to use a different built-in result type: NotFoundObjectResult(message).
Needed to return the error message for 404 Not Found and I am using Dot Net 6.0.
This is the code
Problem(statusCode: 404, detail: "Put your detailed error message here");
Where Problem is a method present in ControllerBase class.

ServiceStack JsonServiceClient Send using route attributes throws exception not found

I am trying to make a simple api post call using servicestack and it keeps throwing an exception "not found". When the same post call is made directly to the api using a web browser rest api e.g. postman, the api call works.
I have decorated my request object with the route attributes
[Route("/register", "POST")]
public class Register : IReturn<RegistrationResponse>
{
public DateTime? BirthDate { get; set; }
public string Continue { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string Password { get; set; }
}
The JsonServiceClient is initialised with the base uri but the following call fails
_client = new JsonServiceClient(_apiUri);
_client.HttpMethod = HttpMethods.Post;
var response = _client.Send(body);
The exception that I catch is:
$exception {"Not Found"} System.Exception
{ServiceStack.ServiceClient.Web.WebServiceException} at
ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowWebServiceException[TResponse](Exception
ex, String requestUri) at
ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowResponseTypeException[TResponse](Object
request, Exception ex, String requestUri) at
ServiceStack.ServiceClient.Web.ServiceClientBase.HandleResponseException[TResponse](Exception
ex, Object request, String requestUri, Func1 createWebRequest, Func2
getResponse, TResponse& response) at
ServiceStack.ServiceClient.Web.ServiceClientBase.Send[TResponse](Object
request) at
ApiService`2.Post(String
path, TParams body) in
ApiService.cs:line 81
The documentation on the new API at servicestack mentions the use of the Route attributes decorating the request DTO and the use of the IReturn but from looking at the code behind the Send method, it is working out the rest api url from the name of the request, which implies that your request dto cannot be named anything different.
public virtual TResponse Send<TResponse>(object request)
{
var requestUri = this.SyncReplyBaseUri.WithTrailingSlash() + request.GetType().Name;
var client = SendRequest(requestUri, request);
try
{
var webResponse = client.GetResponse();
return HandleResponse<TResponse>(webResponse);
}
catch (Exception ex)
{
TResponse response;
if (!HandleResponseException(ex,
request,
requestUri,
() => SendRequest(HttpMethods.Post, requestUri, request),
c => c.GetResponse(),
out response))
{
throw;
}
return response;
}
}
What is causing the Not Found exception?
Everthing in your Register class looks correct.
For your client call I would change it to
_client = new JsonServiceClient(_apiUri);
_client.Post(new Register()); //assuming you can map your 'body' variable to a Register class
Just to lose the extra line of code.
it is working out the rest api url from the name of the request, which implies that your request dto cannot be named anything different.
It is working out the endpoint that the rest api will hit. Once it hits the endpoint, the internals of ServiceStack should handle the routing based on the Operation (in this case Register) and Http method. Basically it will try to find a Service class (any class inheriting the Service marker interface) that has the request object (Register) as a parameter and it will use the Http method as the 'function' to call.
What is causing the Not Found exception?
Not exactly sure about this. If you could provide your 'Service' class it may help.
If you have a Service class like
public class RegisterService : Service
{
public RegistrationResponse Post(Register request)
{
//service code
return new RegistrationResponse();
}
}
the routing should work.
The fix for this was to ensure that the servicestack feature for predefined routes was enabled on the api. Once this is done, you don't need to bother with the Route attribute on the request objects.
The end point host config now looks like this:
new EndpointHostConfig
{
DefaultContentType = ContentType.Json,
EnableFeatures = Feature.None
.Add(Feature.Json)
.Add(Feature.PredefinedRoutes),
GlobalResponseHeaders = new Dictionary<string, string>(),
DefaultRedirectPath = "/documentation"
}

Categories

Resources