I have multiple methods with different signature, and each method has a try-catch block with custom log exception. (Same structure on multiple controllers).
public class TestController : BaseController
{
public static ActionResult One(int param1, string param2)
{
try
{
// Do something
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View("ViwName1");
}
public static ActionResult Two(Date param3, bool param4)
{
try
{
// Do something
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View("ViwName2");
}
}
I wonder if there's a way to avoid try-catch block for every method and execute another
public class TestController : BaseController
{
public static ActionResult One(int param1, string param2)
{
// Do something (*)
// Call "ActionWithTryCatch" method that has a "function argument" to "Do something (*)"
}
public ActionResult ActionWithTryCatch(MyDelegate del, string viewName)
{
try
{
return del.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View(viewName);
}
}
¿How can I do that? I've seen examples using delegates but I understand that's strongly typed, so didn't find a way to do that. Thanks!
The pattern that you are describing is close to a form of Aspect Oriented Programming (AOP). However, if you just want to apply specific Try Catch error handling logic to all actions on your controller then it's probably not worth it for you to pull in a whole AOP framework. Instead you could leverage the HandleErrorAttribute or override the OnException method of the controller class.
For example your could write your controller like this:
public class TestController
{
private TestService service;
public TestController(TestService service)
{
this.service = service;
}
public ActionResult One(int param1, string param2)
{
this.service.MethodOne(param1, param2);
return View("ViwName1");
}
public ActionResult Two(Date param3, bool param4)
{
this.service.MethodTwo(param3, param4);
return View("ViwName2");
}
protected override void OnException(ExceptionContext filterContext)
{
LogException(filterContext.Exception.Message);
AddModelError(filterContext.Exception.Message);
var errorView = new ViewResult { ViewName = "~/Path/To/Error/View" };
filterContext.Result = errorView;
}
}
If you wanted to abstract it out even more then you could just move the overridden OnException logic into a base controller class and then have all of your controller inherit from the base controller.
If you want to see some additional methods of unified error handling in MVC then check out this blog too: https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
UPDATE
Per my comment, if you insist on implementing the pattern you are describing you could use this modified version of gilmishal's answer.
public class TestController
{
private TestService service;
public TestController(TestService service)
{
this.service = service;
}
public ActionResult One(int param1, string param2)
{
return this.ActionWithTryCatch(() => this.service.MethodOne(param1, param2), "ViwName1");
}
public ActionResult Two(Date param3, bool param4)
{
return this.ActionWithTryCatch(() => this.service.MethodTwo(param3, param4), "ViwName2");
}
public IActionResult ActionWithTryCatch(Action action, string viewName)
{
try
{
action.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View(viewName);
}
}
this method you created won't work properly if you need to pass parameters to this function in js - so I am assuming you are calling only parameterless methods.
in that case you could use Func<IActionResult> instead of MyDelegate.
public TResult ActionWithTryCatch<TResult>(Func<TResult> del, string viewName)
{
try
{
return del.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
throw;
}
}
this will be more similar to your javascript implementation, and will return a 500 http result upon unhandledexception.
You should call it like this, if you want IActionResult return type -
ActionWithTryCatch<IActionResult>(MethodThatReturnsIActionResult, viewName);
I recommand you look into generics
Related
I'm trying to invoke a Blazor method in JavaScript inside of an OnSuccess callback for the Plaid API.
Here's the JavaScript that's being run:
async function InitializePlaidLink(objRef, linkToken) {
//console.log("linkToken:" + linkToken);
const handler = Plaid.create({
token: linkToken,
onSuccess: (public_token, metadata) => {
//console.log("public_token: ");
//console.log(public_token);
objRef.invokeMethodAsync('OnPlaidLinkSuccess', public_token);
//console.log("After Invoke Method Async")
},
onLoad: () => {},
onExit: (err, metadata) => {},
onEvent: (eventName, metadata) => {},
//required for OAuth; if not using OAuth, set to null or omit:
//receivedRedirectUri: window.location.href,
});
handler.open();
}
Here's the Blazor code being used:
private string LinkToken { get; set; } = string.Empty;
private string PublicToken { get; set; } = string.Empty;
private async Task InitializePlaid()
{
this.LinkToken = await this.apiService.GetPlaidLinkToken();
var dotNetReference = DotNetObjectReference.Create(this);
await this.jsRuntime.InvokeVoidAsync
(
"InitializePlaidLink",
dotNetReference,
this.LinkToken
);
}
[JSInvokable]
public void OnPlaidLinkSuccess(string publicToken)
{
this.PublicToken = publicToken;
}
The Blazor method InitializePlaid is being called to invoke the JS method InitializePlaidLink. Then, on success, the Blazor method OnPlaidLink Success should be called.
I used log statements to confirm that there is a public_token and the JS after the objRef.invokeMethodAsync() is being reached. Also I was able to invoke a Blazor method in a similar way with a different JS method, just not a method with the Plaid API and the onSuccess callback.
The problem is that the OnPlaidLinkSuccess method must be static as follows:
[JSInvokable]
public static void OnPlaidLinkSuccess(string publicToken)
{
this.PublicToken = publicToken;
}
If you have to define a non-static function, then it will be a bit more complicated.
In this case, it is necessary to send a reference of the current component to the JavaScript method. What follows is a solution that you have to adapt yourself with your own codes.
For this reason, I first create this reference using the DotNetObjectReference.Create method and then send it to the JavaScript code using the jSRuntime.InvokeVoidAsync method. In the example below, JsSample is the name of the current component.
Also defined here, onclick refers to a method inside this component.
This reference should also be disposed at the end of the component's work. That's why you see #IDisposable implements.
#page "/js-sample"
#implements IDisposable
#inject IJSRuntime jSRuntime
<button class="btn btn-primary" #onclick="CallInstanceMethod">Invoke Instance Method</button>
#code
{
private DotNetObjectReference<JsSample> objectReference;
[JSInvokable]
public string GetAddress()
{
return "123 Main Street";
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
objectReference = DotNetObjectReference.Create(this);
}
}
private async Task CallInstanceMethod()
{
await jSRuntime.InvokeVoidAsync("JsFunctionHelper.invokeDotnetInstanceFunction", objectReference);
}
public void Dispose()
{
objectReference?.Dispose();
}
}
Now the javascript code that uses this receiving slot will be as follows. In these codes, addressProvider is the received objectReference that can be used to call the component's non-static GetAddress method:
window.JsFunctionHelper = {
invokeDotnetInstanceFunction: function (addressProvider) {
addressProvider.invokeMethodAsync("GetAddress").then((data) => {
console.log(data);
});
}
};
OnPlaidLinkSuccess was being called correctly. The property this.PublicToken was being correctly updated, but the DOM was not being updated for the user to see. Calling this.StateHasChanged() fixed this.
I have a Controller with the following method:
public void ExportList()
{
var out = GenExport();
CsvExport<LiveViewListe> csv = new CsvExport<LiveViewListe>(out);
Response.Write(csv.Export());
}
this should generate a csv file which the user can download.
I call this method via a jQuery request in my view:
$.getJSON('../Controller2/ExportList', function (data) {
//...
});
the problem is, that I don't get any download and I don't know why. The method is called but without a download.
What is wrong here?
Your controller methods need to always return an ActionResult. So the method should look more like
public ActionResult ExportList()
{
var export = GenExport();
CsvExport<LiveViewListe> csv = new CsvExport<LiveViewListe>(export);
return new CsvResult(csv);
}
Where CsvResult is a class inheriting from ActionResult and doing the necessary to prompt the user for download of your Csv results.
For example, if you really need to Response.Write this could be:
public class CsvResult : ActionResult
{
private CsvExport<LiveViewListe> data;
public CsvResult (CsvExport<LiveViewListe> data)
{
this.data = data;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "text/csv";
response.AddHeader("Content-Disposition", "attachment; filename=file.csv"));
if (data!= null)
{
response.Write(data.Export());
}
}
}
You could also think about making this more generic, if your CsvExport class has the Export method:
public class CsvResult<T> : ActionResult
{
private CsvExport<T> data;
public CsvResult (CsvExport<T> data)
{
this.data = data;
}
.... same ExecuteResult code
}
Now it supports any of your csv downloads, not just LiveViewListe.
Whenever I'm sending JSON from client to server and it tries to convert this JSON into java object. It is giving following exception
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document: N/A\n at [Source:
java.io.PushbackInputStream#18f89001; line: 1, column: 1102] (through
reference chain:
Request["fields"]->java.util.ArrayList[0]->Field["value"]); nested
exception is com.fasterxml.jackson.databind.JsonMappingException
My java code:
public class Field implements Serializable {
private String name;
private String value;
public Field(){
}
public Field(String name, String value ) {
this.name = name;
this.value = value;
}
public String getName(){
return name;
}
public String getValue(){
return value;
}
public void setValue(String newValue) throws Exception {
someValidation();//Some validation on value
value = newValue;
}
public void setName(String name) {
this.name = name;
}
}
public class Request implements Serializable{
private String name;
private List<Field> fields;
public Request() {
}
public String getName() {
return viewName;
}
public void setName(String name) {
this.name = name;
}
public List<Field> getFields() {
return fields;
}
public void setField(List<Field> fields) {
this.fields = fields;
}
}
My JSON is :
{
"name" : "name0",
"fields" : [{"name":"abc0", "value":"0"}, {"name":"def0", "value":"0"}]
}
Your code looks fine, I think the problem might be with your request header, which can confuse the server.
I suggest you try to post a simpler JSON to your server, maybe just a string and find out step by step where is your failure point, check your request and maybe simulate one with POSTMAN or something familiar to make sure the client isn't the problem here.
How can I call a generic WebAPI action from JavaScript? and pass the T type.
C#:
public class ClassUtilitiesController : ApiController
{
[HttpPost]
public string GetClassNamespace<T>()
{
return typeof(T).FullName;
}
}
JavaScript:
$.post('/api/ClassUtilities', {
T: 'string',
// ...
});
I need an action that returns something from input T type (ex. typeof(T).FullName or Create an instance and call a method). What should I do?
The JSON data of the POST request it's actually a string, meaning that you have to deserialize is as the object you want.
Try:
public class ClassUtilitiesController : ApiController
{
[HttpPost]
public string GetClassNamespace(string data)
{
T t;
try
{
t = JsonConvert.DeserializeObject<T>(data);
}
catch (Exception e)
{
return e;
}
return t.FullName;
}
}
I'm trying to think of a pattern I can use.
I want to be able to have a middleman module that takes in a kind of state of a game. Given that state, call a certain method that resides in anotehr module.
What pattern could I use for this?
For example, I want to be able to take in a state of "computer always wins" and based on that state type, I'll call someOtherModule.makeComputerMove(). In the future maybe we want to be able to set the game to a mode where the computer doesn't always win. Well then we could send in a state of "normal game" or something like that which would just call computerAlwaysWins.makeComputerMove() from a different use case module such as normalGame.makeComputerMove()
get the idea?
I can't think of any patterns to provide such a thing...probably because I don't know a lot of them.
You should use State pattern probably with combination of Observer.
public class GameStateContext {
PlayerState Player {get;set; }
// other properties that need to be shared
}
public interface IGameController {
void GoToState(State state)
}
public interface IGameState {
void Start();
void Update();
}
public abstract class GameStateBase : IGameState {
protected GameStateContext _context;
protected IGameController _parent;
public GameStateBase(GameStateContext context, IGameController parent) {
this._context = context;
this._parent = parent;
}
public virtual void Start() {
}
public virtual void Update() {
}
}
public class BonusLevelState : GameStateBase {
public public MainMenuState (GameStateContext context, IGameController parent) : base (context, parent) {
}
public override void Update() {
if(_context.Player.Health == 0) {
_parent.GoToState(GameStates.GameOver);
}
}
}
public GameController : IGameController {
public enum GameStates {
BonusLevel,
InitialState,
....
}
private IGameState currentState;
public GameController() {
// create diferent states
...
currentState = GetState(GameStates.InitialState);
}
public void Update {
currentState.Update();
}
public GoToState(State state) {
currentState = GetState(state);
}
}
I hope you catch an idea, good luck!