I'm trying to make a simple REST service in VS2010 (.NET 4) with one method that receives three string parameters. I want it to be accessed using both GET and POST (because 3'rd param could be quite big sometimes)
In class MyREST.cs I have this code
[WebGet(UriTemplate = "s={s}&sp={sp}&p={p}")]
public string Process(string s, string sp, string p)
{
// some processing
return result;
}
Same for POST
When I try to access the service with an url like
http://localhost:57129/OneTestREST/s=str1&sp=str2&p=str3
I get error
Exception Details: System.Web.HttpException: A potentially dangerous
Request.Path value was detected from the client (&).
If I change the attribute to
[WebGet(UriTemplate = "/{s}/{sp}/{p}")]
It works ok. But I would like to access it with first syntax
Any idea why is this happening?
Thank you
[WebGet]
public string Process(string s, string sp, string p)
{
// some processing
return result;
}
then
http://localhost:57129/OneTestREST/?s=str1&sp=str2&p=str3
Related
I am new to Spring MVC, I have a controller displaying a single user detail or
all the users as a list
// without passing any parameter the app should display all users
#RequestMapping("/user")
public String listUsers(Model m) {
List<User> users = userService.getUsers();
m.addAttribute(users);
return "userlist";
}
//same url but with userId parameter, the app displays a single user detail
#RequestMapping("/user")
public String userDetail(#RequestParam("userId") String userId, Model m)
throws IOException {
User user = userService.getUserById(userId);
m.addAttribute(user);
return "user_detail";
}
in fact I got a error "spring ambiguous mapping", my mapping syntax is definitely wrong, my question is my desired functionality can be achieved
in Spring or not.
I would say it would be correct to specify list request as
#RequestMapping("/user")
and specific user request as
#RequestMapping("/user/{userId}")
by passing userId as path parameter.
You can correct second method like
#RequestMapping(value={"/user/{userId}"})
public String userDetail(#PathVariable String userId, Model m)
throws IOException {
User user = userService.getUserById(userId);
m.addAttribute(user);
return "user_detail";
}
I have this Object
public class Deportista implements Serializable {
private static final long serialVersionUID = 6229604242306465153L;
private String id;
...
#NotNull(message="{field.null}")
public String getId() {
return id;
}
...
}
I have the following Controller's methods
#InitBinder(value="deportistaRegistrar")
public void registrarInitBinder(WebDataBinder binder) {
logger.info(">>>>>>>> registrarInitBinder >>>>>>>>>>>>>");
}
#RequestMapping(value="/registrar.htm", method=RequestMethod.GET)
public String crearRegistrarFormulario(Model model){
logger.info("crearRegistrarFormulario GET");
Deportista deportista = new Deportista();
model.addAttribute("deportistaRegistrar", deportista);
return "deportista.formulario.registro";
}
#RequestMapping(value="/registrar.htm", method=RequestMethod.POST)
public String registrarPerson(#Validated #ModelAttribute("deportistaRegistrar") Deportista deportista,
BindingResult result){
logger.info("registrarPerson POST");
logger.info("{}", deportista.toString());
if(result.hasErrors()){
logger.error("There are errors!!!!");
for(ObjectError objectError : result.getAllErrors()){
logger.error("Error {}", objectError);
}
return "deportista.formulario.registro";
}
logger.info("All fine!!!!");
this.fakeMultipleRepository.insertDeportista(deportista);
return "redirect:/manolo.htm";
}
Until here the Controller is able to create a form (GET) and submit (POST) a new command object, Validation code works well.
The problem is with the update.
I have the following:
#InitBinder(value="deportistaActualizar")
public void actualizarInitBinder(WebDataBinder binder) {
logger.info(">>>>>>>> actualizarInitBinder >>>>>>>>>>>>>");
binder.setDisallowedFields("id");
}
Observe I have binder.setDisallowedFields("id")
public String crearActualizarFormulario(#PathVariable("id") String id, Model model){
logger.info("crearActualizarFormulario GET");
Deportista deportista = this.fakeMultipleRepository.findDeportista(id);
model.addAttribute("deportistaActualizar", deportista);
return "deportista.formulario.actualizacion";
}
#RequestMapping(value="/{id}/actualizar.htm", method=RequestMethod.POST)
public String actualizarPerson(#Validated #ModelAttribute("deportistaActualizar") Deportista deportista,
BindingResult result){
logger.info("actualizarPerson POST");
logger.info("{}", deportista.toString());
if(result.hasErrors()){
logger.error("There are errors!!!!");
for(ObjectError objectError : result.getAllErrors()){
logger.error("Error {}", objectError);
}
return "deportista.formulario.actualizacion";
}
logger.info("All fine!!!!");
this.fakeMultipleRepository.updateDeportista(deportista);
return "redirect:/manolo.htm";
}
The problem is:
when the form or command has any error, the controller re-render the view and the form appear showing the error messages how is expected, but without the ID value
or
if I try to update the object, of course keeping the id value, and without any error to simply proceed to update, it fails
The following appears in the Console:
- -------- createCollections ---------------
- >>>>>>>> actualizarInitBinder >>>>>>>>>>>>>
- Skipping URI variable 'id' since the request contains a bind value with the same name.
- actualizarPerson POST
- Deportista [id=null, nombre=Manuel, ...]
- There are errors!!!!
- Error Field error in object 'deportistaActualizar' on field 'id': rejected value [null]; codes [NotNull.deportistaActualizar.id,NotNull.id,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [deportistaActualizar.id,id]; arguments []; default message [id]]; default message [The field must be not empty]
The id is null. How I can around this problem keeping the Request Scope?
I have an alternate controller which is working with #SessionAttributes and all works perfect. But since is a huge risk if the user has many tabs open in the same web browser, one for create and other for updating, all is going to be very wrong. According with Spring MVC + Session attributes and multiple tabs, request scope should be used instead of session scope. It has sense.
Sadly seems Spring is not going to fix this:
#SessionAttributes doesn't work with tabbed browsing
Addition
According with your suggestion, I have the following:
#ModelAttribute("deportistaActualizar")
public Deportista populateActualizarFormulario(#RequestParam(defaultValue="") String id){
logger.info("populateActualizarFormulario - id: {}", id);
if(id.equals(""))
return null;
else
return this.fakeMultipleRepository.findDeportista(id);
}
Observe the method uses #RequestParam, my problem is how update that method to work when the URL to update has the following style
http://localhost:8080/spring-utility/deportista/1/actualizar.htm. There is no param in the URL, therefore #RequestParam is useless now.
I already have read the Spring Reference documentation:
Using #ModelAttribute on a method
Second Addition
Yes, you was right, and I did that yesterday, but I forget to share the following:
#ModelAttribute("deportistaActualizar")
public Deportista populateActualizarFormulario(#PathVariable(value="id") String id){
logger.info("populateActualizarFormulario - id: {}", id);
if(id.equals(""))
return null;
else
return this.fakeMultipleRepository.findDeportista(id);
}
Since a #ModelAttribute is called always before by any handler method, the following URL fails http://localhost:8080/spring-utility/deportista/registrar.htm, the following appears on the page
HTTP Status 400 -
type Status report
message
description The request sent by the client was syntactically incorrect.
Of course because the URL does not contains the expected id. Therefore I can't create new records to later edit/see.
I can confirm, that for the following work:
http://localhost:8080/spring-utility/deportista/1/detalle.htm
http://localhost:8080/spring-utility/deportista/1/actualizar.htm
the id (1) is retrieved.
How I could resolve this?
Thank You
When calling an extension method from another extension method, my solution was building ok, but in the published site (or the virtual asp.net server) I was getting the Compile Error "Ambiguous call".
public static string ExtensionMethodA(this ObjectToExtend myObj){//code here}
public static string ExtensionMethodB(this ObjectToExtend myObj){
string a = myObj.ExtensionMethodA(); // this line causes the error.
return a;
}
I haven't read enough to know exactly why, but here is the solution:
public static string ExtensionMethodA(this ObjectToExtend myObj){//code here}
public static string ExtensionMethodB(this ObjectToExtend myObj){
string a = ExtensionMethodA(myObj); // correct call.
return a;
}
I have the following method skeleton in a Spring MVC application:
#RequestMapping(value = "/activateMember/{token}", method = RequestMethod.GET, produces = "text/html")
public String activateMember(#PathVariable("token") String token) {
...
}
I am trying to display an error message if the token is invalid for some reason. However I have no ModelAttribute in the method arguments and I don't really want one. But of course I can't use an Errors or BindingResults argument because of the absence of a ModelAttribute and its corresponding form.
So my question is:
what is the recommended way to display an error message given the above method signature and without introducing a ModelAttribute?
If the String you've returned from the method is a viewname (Spring default) then simply create a view for this case and do like:
#RequestMapping()
public String activateMember(#PathVariable("token") String token) {
if(checkToken(token)){
doProcess();
return "userprofile";
} else {
return "badtoken"
}
}
In more complicated case you may have a hierarchy of exceptions, related to bad tokens. (Token is expired, token is just incorrect and so on). You can register an #ExceptionHandler in the same controller:
#RequestMapping()
public String activateMember(#PathVariable("token") String token) {
return activate(token); // This method may throw TokenException and subclasses.
}
#ExceptionHandler(TokenException.class)
public ModelAndView tokenException(TokenException e){
// some code
return new ModelAndView("badtoken", "exception", e);
}
I have an ASP.NET Web Forms application with UI, Service layer and Repository layer.
Some of the methods in my Service Layer communicates with a Web Service, therefore I would like to wrap all the calls to the Web Methods in a Try-Catch-Finally construct.
Suppose I have the following methods in my Service Layer:
public RegistrationDetails GetRegistrationDetails(int userId)
public bool RegisterUser(UserData myUserData)
Where RegistrationDetails and myUserData are object types (classes).
My concern is the following: if I create a Try-Catch-Finally to wrap the call to the Web Service within the implementation of the methods listed above, in case there is an exception how can I return the message string if the return types are RegistrationDetails and bool?
I was thinking about adding a property to every return object but I do not know if that is a good solution. For instance instead of using bool:
public class RegisterResponse
{
public bool isRegistered { get; set; }
public string ExceptionMessage { get; set; }
}
public RegisterResponse RegisterUser(UserData myUserData)
And then check if ExceptionMessage is null or String.Empty. Is it a good approach? Thanks
1) As mentioned by IrishChieftain, bubbling the exception down to the forms is good, you will be able to respond to the exception better
2) You can also have a reference parameter as array which stores exception messages generated from the method
public bool RegisterUser(UserData myUserData, optional ref ArrayList<string> errors)
{
if(error)
errors.Add("This error occured")
}
3) For Instance Object, you could have an Instance variable of ArrayList for errors and have that returned in a property
public class MyClass
{
private ArrayList<string> errors = new ArrayList<string>
public ArrayList<string> ExceptionMessages()
{
get
{
return errors;
}
}
public RegistrationDetails GetRegistrationDetails(int userId) { }
}
//Used like this:
MyClass c = new MyClass();
c.GetRegistrationDetails();
if(c.ExceptionMessages > 0)
{
//output errors
}
But I would prefer the first approach - for flexibility like output formatting
Passing raw exceptions on to your client (web forms layer) from a service could be risky. If it's a database exception, it might expose details of your database. A malicious user might call your service layer methods from their own application.
You can expect two types of exceptions on the client level:
Communication Exception (problems connecting to the service)
Server-side error (database problem, username not unique, password invalid... other business rule exceptions)
The first type should be caught in try-catch-finally in your web forms layer, but the second kind should be caught in the service layer, logged, then wrapped up in the RegisterResponse object, as you suggest. But, instead of sending Exception.Message, you might consider using an enum of expected errors (with a ServerError member to cover anything else.) You could also add an EventId to the response and log entry so that you can investigate errors.
public enum RegisterResponseError { NoError = 0, SystemError = 1,
UserNameNotUnique, PasswordInvalid, etc. }
public class RegisterResponse
{
public bool isRegistered { get; set; }
public RegisterResponseError ErrorCode { get; set; }
}
Then in your client code,
if(myRegisterResponse.ErrorCode == RegisterResponseError.NoError)
// everything was fine
else
// show a suitable error message for ErrorCode and display EventId (if logging)
You could return an error string from your service, but it's probably better to manage any content in your web forms layer, in case you need to localize the content or use a CMS later on.