I have this method in an API that does POST for creating a record, but before inserting that record in DB there are some validations that i must do, I might come back with warnings and I need to return back these warnings to the client to confirm back.
what is the best way to do that in Web API's? or should i split the method into 2, one for validation and one for saving?
Set up your Web Api the following way.
[HttpPost]
public IHttpActionResult DoStuff(object item)
{
if(!validate(item))
{
return this.BadRequest("Validation failed blablabla");
}
else
{
//insert logic
}
return this.Ok();
}
What happens is you validate the object you send to the API. When it fails to validate you return the request was not correct, with the message you specified.
When the validation succeeds the insert logic is called and you return a OK result when it succeeds.
I create and use a wrapper which would contain object to be bound to ui, messages (error or validation or warning) and total.
/// <summary>
/// Class for the Repository response object
/// </summary>
/// <typeparam name="T"></typeparam>
public class CLSResponse<T>
{
/// <summary>
/// Gets or sets the messages to be returned in the response.
/// </summary>
/// <value>The messages.</value>
public IEnumerable<KeyValuePair<string, string>> Messages {
get { return m_Messages; }
set { m_Messages = value; }
}
private IEnumerable<KeyValuePair<string, string>> m_Messages;
/// <summary>
/// Gets or sets the service model to be returned in the response.
/// </summary>
/// <value>The service model.</value>
public T ServiceModel {
get { return m_ServiceModel; }
set { m_ServiceModel = value; }
}
private T m_ServiceModel;
/// <summary>
/// Gets or sets the totalitems.
/// </summary>
/// <value>The TotalItems.</value>
public int TotalItems {
get { return m_TotalItems; }
set { m_TotalItems = value; }
}
private int m_TotalItems;
/// <summary>
/// Gets and Sets the Message Type based on the MessageType Struct
/// </summary>
public string MessagesType;
}
/// <summary>
/// Struct for MessageTypes to be returned with messages, in the response object
/// </summary>
public struct MessagesType
{
/// <summary>
/// Validation
/// </summary>
public static string Validation = "Validation";
/// <summary>
/// Warning
/// </summary>
public static string Warning = "Warning";
/// <summary>
/// Error
/// </summary>
public static string Error = "Error";
/// <summary>
/// Unauthorized
/// </summary>
public static string UnAuthorized = "Unauthorized";
}
Then in your repository layer or logic layer
public CLSResponse<bool> CreateUser(string request)
{
var messages = new List<KeyValuePair<string, string>>();
try
{
//Do something
if (!validation)
{
messages.Add(MessagesType.Validation, "Invalid");
return new CLSResponse<bool> {
ServiceModel = false,
Messages = messages,
MessagesType = MessagesType.Validation
};
}
else {
return new CLSResponse<bool> {
ServiceModel = true,
Messages = messages,
MessagesType = MessagesType.Error
};
}
}
catch (Exception ex)
{
messages.Add(MessagesType.Error, "UpdateFailed");
return new CLSResponse<bool> {
ServiceModel = false,
Messages = messages,
MessagesType = MessagesType.Error
};
}
}
Now on controller,
[HttpPost]
public HttpResponseMessage<CLSResponse<bool>> CreateUser(string input)
{
var res = LogicLayer.CreateUser(input);
//Check for res.MessageType and set the status code
if (res.MessagesType = MessagesType.Validation)
{
return Request.CreateResponse<CLSResponse<bool>>(HttpStatusCode.PreconditionFailed, res);
}
}
This way your response object still has the warning you added in logic layer and depending on the status code returned, you can handle those messages on client side.
Related
Is there a way to have multiple OData endpoints/functions return different types mapped to a single ODataController? The normal crud operations map to an endpoint odata/foo but if I wanted to add more methods to the controller can I define a function that returns a different type under odata/foo/foosummaries?
Could I add more entity sets that would map to the same controller class? For example if I wanted to have an endpoint under my FooController that returned FooSummaries that had the endpoint ~/odata/Foo/FooSummaries?
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion, string routePrefix)
{
builder.EntitySet<FooResponse>("Foos");
builder.EntityType<FooSummaries>()
.Function(nameof(SubmissionsController.GetFooSummaries))
.ReturnsCollection<IEnumerable<FooSummaryREsponse>>();
}
Would I need to decorate the OData controller differently with ODataPrefix or ODataRoute?
public class SubmissionsController : ODataController
{
#region Private Fields
private ISubmissionManager _manager;
private ILogger _logger;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
/// <param name="manager">A SubmissionManager instance.</param>
/// <param name="logger">A Logger instance.</param>
public SubmissionsController(ISubmissionManager manager, ILogger logger)
{
_manager = manager;
_logger = logger;
}
#endregion
#region HttpGet Endpoints
/// <summary>
/// Odata get all foos
/// </summary>
/// <returns></returns>
[HttpGet()]
[Produces("application/json")]
[ProducesResponseType(200, Type = typeof(IQueryable<FooResponse>))]
[ProducesResponseType(400, Type = typeof(ProblemDetails))]
[ProducesResponseType(404, Type = typeof(ProblemDetails))]
[ProducesResponseType(500, Type = typeof(ProblemDetails))]
[EnableQuery(PageSize = 20)]
public IActionResult Get()
{
try
{
IQueryable<FooResponse> result = _manager.GetFoos();
return Ok(result);
}
catch (FooException ex)
{
return ex.GetObjectResult(this);
}
}
/// <summary>
/// Get an odata queryable foo summary
/// </summary>
/// <param name="query">The query arguments</param>
/// <returns></returns>
[HttpGet("api/v{version:apiVersion}/odata/Foos/summaries")]
[Produces("application/json")]
[ProducesResponseType(200, Type = typeof(IEnumerable<FooSummaryResponse>))]
[ProducesResponseType(400, Type = typeof(ProblemDetails))]
[ProducesResponseType(404, Type = typeof(ProblemDetails))]
[ProducesResponseType(500, Type = typeof(ProblemDetails))]
[EnableQuery()]
public IActionResult GetFooSummaries()
{
try
{
IQueryable<FooSummaryResponse> results = _manager.GetFooSummaryResponse();
results);
return Ok(results);
}
catch (FooException ex)
{
return ex.GetObjectResult(this);
}
}
}
I've seen some other SO's where people are using DateTimeOffset surrogates to handle deserializing those properties, however when I try to copy those, I continue to get a System.InvalidOperationException: No serializer defined for type: System.DateTimeOffset error.
[ProtoContract]
public TestClass
{
[ProtoMember(1)]
public DateTimeOffset Time { get; set; }
}
Surrogate class
[ProtoContract]
public class DateTimeOffsetSurrogate
{
[ProtoMember(1)]
public long DateTimeTicks { get; set; }
[ProtoMember(2)]
public short OffsetMinutes { get; set; }
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
{
return new DateTimeOffsetSurrogate
{
DateTimeTicks = value.Ticks,
OffsetMinutes = (short)value.Offset.TotalMinutes
};
}
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
{
return new DateTimeOffset(value.DateTimeTicks, TimeSpan.FromMinutes(value.OffsetMinutes));
}
}
Then I'm registering it right before the http call. I've tried moving this registration into a few different places but it doesn't seem to make a difference. Did this change in v3 or something or am I doing something wrong? Sorry - new to protobuf-net :)
public async Task<Response<IEnumerable<TestClass>>> GetData()
{
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
var request = new HttpRequestMessage(HttpMethod.Get, "my-url");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-protobuf"));
var result = await _httpClient.SendAsync(request);
var items= ProtoBuf.Serializer.Deserialize<Response<IEnumerable<TestClass>>>(await result.Content.ReadAsStreamAsync());
return items;
}
I am using version 3.1.22 (Currently the latest) of protobuf-net right now with the following setup.
// Needs to be only called at application startup, e.g. in the Startup.cs calls of your web API.
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset?), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
The surrogate handler I use successfully, can be found below. No black magic happening here, just serializing / deserializing the unix timestamp to long and vice versa:
namespace Something
{
using System;
using ProtoBuf;
/// <summary>
/// A surrogate handler for the <see cref="DateTimeOffset"/> class.
/// </summary>
[ProtoContract(Name = nameof(DateTimeOffset))]
public class DateTimeOffsetSurrogate
{
/// <summary>
/// Gets or sets the value.
/// </summary>
[ProtoMember(1)]
public long? Value { get; set; }
/// <summary>
/// Converts the <see cref="DateTimeOffsetSurrogate"/> to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="surrogate">The surrogate handler.</param>
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate surrogate)
{
if (surrogate?.Value is null)
{
throw new ArgumentNullException(nameof(surrogate));
}
var dt = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
dt = dt.ToLocalTime();
return dt;
}
/// <summary>
/// Converts the <see cref="DateTimeOffsetSurrogate"/> to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <param name="surrogate">The surrogate handler.</param>
public static implicit operator DateTimeOffset?(DateTimeOffsetSurrogate? surrogate)
{
if (surrogate?.Value is null)
{
return null;
}
var dt = DateTimeOffset.FromUnixTimeMilliseconds(surrogate.Value.Value);
dt = dt.ToLocalTime();
return dt;
}
/// <summary>
/// Converts the <see cref="DateTimeOffset"/> to a <see cref="DateTimeOffsetSurrogate"/>.
/// </summary>
/// <param name="source">The source.</param>
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset source)
{
return new DateTimeOffsetSurrogate
{
Value = source.ToUnixTimeMilliseconds()
};
}
/// <summary>
/// Converts the <see cref="DateTimeOffset"/> to a <see cref="DateTimeOffsetSurrogate"/>.
/// </summary>
/// <param name="source">The source.</param>
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset? source)
{
return new DateTimeOffsetSurrogate
{
Value = source?.ToUnixTimeMilliseconds()
};
}
}
}
Maybe, you want to give it a try. Maybe [ProtoContract(Name = nameof(DateTimeOffset))] is what you're missing, but I'm not sure.
I've seen examples of using some of the new IHttpActionResults for OK, NotFound.
I haven't seen anything using Unauthorized().
my existing code looks like:
catch (SecurityException ex)
{
request.CreateResponse(HttpStatusCode.Unauthorized, ex.Message);
}
I'd like to replace it with this:
catch (SecurityException ex)
{
response = Unauthorized();
}
but I don't see any overload to pass exception details.
Also, what is the IHttpActionResult equivalent of returning a 500 error?
catch (Exception ex)
{
response = request.CreateErrorResponse(HttpStatusCode.InternalServerError,
ex.Message);
}
Per the code for ApiController, there are only two overloads for Unauthorized():
/// <summary>
/// Creates an <see cref="UnauthorizedResult"/> (401 Unauthorized) with the specified values.
/// </summary>
/// <param name="challenges">The WWW-Authenticate challenges.</param>
/// <returns>An <see cref="UnauthorizedResult"/> with the specified values.</returns>
protected internal UnauthorizedResult Unauthorized(params AuthenticationHeaderValue[] challenges)
{
return Unauthorized((IEnumerable<AuthenticationHeaderValue>)challenges);
}
/// <summary>
/// Creates an <see cref="UnauthorizedResult"/> (401 Unauthorized) with the specified values.
/// </summary>
/// <param name="challenges">The WWW-Authenticate challenges.</param>
/// <returns>An <see cref="UnauthorizedResult"/> with the specified values.</returns>
protected internal virtual UnauthorizedResult Unauthorized(IEnumerable<AuthenticationHeaderValue> challenges)
{
return new UnauthorizedResult(challenges, this);
}
So it looks like you are out of luck, unless you want to make the change yourself (fork your own version of WebAPI or do a pull request to attempt to get it into the main branch).
The IHttpActionResult equivalent of returning a 500 error is this:
/// <summary>
/// Creates an <see cref="ExceptionResult"/> (500 Internal Server Error) with the specified exception.
/// </summary>
/// <param name="exception">The exception to include in the error.</param>
/// <returns>An <see cref="ExceptionResult"/> with the specified exception.</returns>
protected internal virtual ExceptionResult InternalServerError(Exception exception)
{
return new ExceptionResult(exception, this);
}
You can read more about Unathorized() here
How do you return status 401 from WebAPI to AngularJS and also include a custom message?
If found another way - without throwing an exception and using HttpResponseMessage as return type of your controller , you can do it like this:
return ResponseMessage(Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "I am unauthorized IHttpActionResult custom message!"));
More about ResponseMessage() here
http://msdn.microsoft.com/en-us/library/system.web.http.apicontroller.responsemessage%28v=vs.118%29.aspx
This is my solution for a 500 exception with custom message (expanded on from here: https://stackoverflow.com/a/10734690/654708)
Base class for all Web API controllers
public abstract class ApiBaseController : ApiController
{
protected internal virtual IHttpActionResult InternalServerError(Exception ex, string message = null)
{
var customMsg = String.IsNullOrWhiteSpace(message) ? "" : String.Format("Custom error message : {0}. ", message);
var errorMsg = String.Format("{0}{1}", customMsg, ex);
return new InternalServerErrorWithMessageResult(errorMsg);
}
}
public class InternalServerErrorWithMessageResult : IHttpActionResult
{
private readonly string message;
public InternalServerErrorWithMessageResult(string message)
{
this.message = message;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(message)
};
return Task.FromResult(response);
}
}
Sample Web API controllers
public class ProductController : ApiBaseController
{
public IHttpActionResult GetProduct(int id)
{
try
{
var product = productService.GetProduct(id);
return Ok(product); // 200 w/ product
} catch(Exception ex)
{
//return InternalServerError(ex); // Uses default InternalServerError
return InternalServerError(ex, "My custom message here"); // Uses custom InternalServerError
}
}
}
When a page loads I start a new task in my hub. This task sends data to a browser updating a certain html elements. When I browse away from the page I want to stop the task.
The problem is that before the task is stopped (due to it's sleep argument), a new tokenSource = new CancellationTokenSource();
is set before the previous instance of this task had a chance to stop.
What i'm trying to do is to have the task stop when browsing away from the page to a different page that is not requiring signalR. BUT maybe, not stop it if it's only a refresh of the same page. Not sure how to do it. To sum it up, I want to guarantee that only 1 instance of this task is running (AND only on the page that requires it/or have a listener)
any info greatly appreciated.
thanks
CODE:
public class TaskActionStatus : Hub
{
#region Static Fields
/// <summary>
/// The token source.
/// </summary>
private static CancellationTokenSource tokenSource;
/// <summary>
/// The url string.
/// </summary>
private static string url = string.Empty;
#endregion
#region Public Methods and Operators
/// <summary>
/// The get tasks status.
/// </summary>
/// <param name="siteId">
/// The site id.
/// </param>
/// <param name="location"></param>
public void GetTasksStatus(int? siteId)
{
var taskRepository = UnityContainerSetup.Container.Resolve<ITaskRepository>();
tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
// init task for checking task statuses
var tasksItem = new DownloadTaskItem();
// start task only if at least one listener
if (UserHandler.ConnectedIds.Count < 2 && !taskRepository.IsTasksStatusAsyncRunning())
{
taskRepository.GetTasksStatusAsync(siteId, tasksItem, ct);
// subscribe to event [ listener ]
tasksItem.Changed += this.UpdateTasksStatus;
}
else tokenSource.Cancel();
}
/// <summary>
/// The on connected.
/// </summary>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
public override Task OnConnected()
{
UserHandler.ConnectedIds.Add(this.Context.ConnectionId);
return base.OnConnected();
}
/// <summary>
/// The on disconnected.
/// </summary>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
public override Task OnDisconnected()
{
UserHandler.ConnectedIds.Remove(this.Context.ConnectionId);
if (UserHandler.ConnectedIds.Count == 0)
{
try
{
tokenSource.Cancel();
}
catch (Exception ex)
{
Log.Error(ex);
}
}
return base.OnDisconnected();
}
/// <summary>
/// The update tasks status.
/// </summary>
/// <param name="sender">
/// The sender.
/// </param>
/// <param name="e">
/// The e.
/// </param>
public void UpdateTasksStatus(object sender, TaskEventArgs e)
{
this.Clients.All.updateMessages(e.Tasks);
}
#endregion
}
/// <summary>
/// The user handler.
/// </summary>
public static class UserHandler
{
#region Static Fields
/// <summary>
/// The connected ids.
/// </summary>
public static HashSet<string> ConnectedIds = new HashSet<string>();
#endregion
}
public bool IsTasksStatusAsyncRunning()
{
if (tasksStatusAsync != null && tasksStatusAsync.Status.Equals(TaskStatus.Running))
{
return true;
}
return false;
}
Moving this line:
tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
...
making it this:
if (UserHandler.ConnectedIds.Count < 2)
{
Trace.WriteLine("GetTasksStatus: Starting new task");
tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
taskRepository.GetTasksStatusAsync(siteId, tasksItem, ct);
// subscribe to event [ listener ]
tasksItem.Changed += this.UpdateTasksStatus;
}
did it for me. thanks
I'm trying to load and invoke activityes from custom activity as follows:
Imagine I have a xamlx like this:
--Sequence
|----- LoadActiviy
|--Initialize dictionary with input data of activitity
|----- Invoke
This works when activity NOT CONTAINS receive/send messages. But when i try with activity wich contains receive/send messages the result is a exception
WorkflowApplicationUnhandledExceptionEventArgs: Only registered bookmark scopes can be used for creating scoped bookmarks.
The code:
1-Load xaml: (Load activity)
public sealed class LoadActivity : CodeActivity<Activity>
{
#region Properties
/// <summary>
/// Gets or sets Path.
/// </summary>
[RequiredArgument]
public InArgument<string> Path { get; set; }
#endregion
#region Methods
/// <summary>
/// The execute method.
/// </summary>
/// <param name="context">
/// The context.
/// </param>
/// <returns>
/// An activity loaded from a file
/// </returns>
protected override Activity Execute(CodeActivityContext context)
{
return ActivityXamlServices.Load(this.Path.Get(context));
}
#endregion
}
2- Run activity:
public class SynchronousSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
{
d(state);
}
}
public sealed class Invoke : CodeActivity
{
#region Properties
/// <summary>
/// Gets or sets Activity.
/// </summary>
/// <remarks>
/// The activity that will be invoked. Can be loaded from XAML.
/// </remarks>
[RequiredArgument]
public InArgument<Activity> Activity { get; set; }
public OutArgument<IDictionary<string, object>> Output { get; set; }
/// <summary>
/// Gets or sets Input.
/// </summary>
/// <remarks>
/// The input arguments you want to pass to the other workflow
/// </remarks>
public InArgument<IDictionary<string, object>> Input { get; set; }
#endregion
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
try
{
IDictionary<string,object> _input= this.Input.Get(context);
foreach (KeyValuePair<string,object> item in _input )
{
Debug.WriteLine(string.Format("{0} {1}", item.Key, item.Value));
}
// AutoResetEvent idleEvent = new AutoResetEvent(false);
WorkflowApplication app = new WorkflowApplication(this.Activity.Get(context),this.Input.Get(context));
app.SynchronizationContext = new SynchronousSynchronizationContext();
app.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
// idleEvent.Set();
};
app.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
e.InstanceId, e.UnhandledException.Message);
Console.WriteLine("ExceptionSource: {0} - {1}",
e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);
// Instruct the runtime to terminate the workflow.
// Other choices are Abort and Cancel. Terminate
// is the default if no OnUnhandledException handler
// is present.
return UnhandledExceptionAction.Terminate;
};
app.Idle = e => Console.WriteLine("WorkflowApplication.Idle called");
Console.WriteLine("Before WorkflowApplication.Run()");
app.Run();
}
catch
{
throw;
}
}
}
Any ideas?
You can only use a Receive activity in a workflow hosted in a WorkflowServiceHost. Even if your main workflow is hosted in a WorkflowServiceHost the child workflow is hosted in a WorkflowApplication and can't contain a Receive activity because it isn't running as part of the WCF infrastructure.